* [PATCH 2/4] public keys: make key_name_hint optional
2026-05-27 10:54 [PATCH 0/4] public keys: rework keyrings as nested containers Sascha Hauer
2026-05-27 10:54 ` [PATCH 1/4] public keys: make error message more informative Sascha Hauer
@ 2026-05-27 10:54 ` Sascha Hauer
2026-05-27 10:54 ` [PATCH 3/4] public keys: rework keyrings as nested containers Sascha Hauer
` (2 subsequent siblings)
4 siblings, 0 replies; 6+ messages in thread
From: Sascha Hauer @ 2026-05-27 10:54 UTC (permalink / raw)
To: BAREBOX
The key_name_hint is merely a hint which key might fit and as such
it should be optional for a key. In keytoc.c only set it when it has
meaningful content and test for non NULL in barebox before dereferencing
it.
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
commands/keys.c | 3 ++-
crypto/public-keys.c | 2 ++
scripts/keytoc.c | 10 ++++------
3 files changed, 8 insertions(+), 7 deletions(-)
diff --git a/commands/keys.c b/commands/keys.c
index 83c3ed533d..12cb6ea2e3 100644
--- a/commands/keys.c
+++ b/commands/keys.c
@@ -9,7 +9,8 @@ static int do_keys(int argc, char *argv[])
for_each_public_key(key, id) {
printf("KEY: %*phN\tTYPE: %s\tKEYRING: %s\tHINT: %s\n", key->hashlen,
- key->hash, public_key_type_string(key->type), key->keyring, key->key_name_hint);
+ key->hash, public_key_type_string(key->type), key->keyring,
+ key->key_name_hint ?: "");
}
return 0;
diff --git a/crypto/public-keys.c b/crypto/public-keys.c
index ecf255bbb4..2b4bac55b7 100644
--- a/crypto/public-keys.c
+++ b/crypto/public-keys.c
@@ -14,6 +14,8 @@ const struct public_key *public_key_get(const char *name, const char *keyring)
int id;
for_each_public_key_keyring(key, id, keyring) {
+ if (!key->key_name_hint)
+ continue;
if (!strcmp(key->key_name_hint, name))
return key;
}
diff --git a/scripts/keytoc.c b/scripts/keytoc.c
index c4491fbe81..40601827b7 100644
--- a/scripts/keytoc.c
+++ b/scripts/keytoc.c
@@ -566,7 +566,8 @@ static int gen_key_ecdsa(EVP_PKEY *key, struct keyinfo *info)
if (!standalone) {
fprintf(outfilep, "\nstatic struct public_key %s_public_key = {\n", info->name_c);
fprintf(outfilep, "\t.type = PUBLIC_KEY_TYPE_ECDSA,\n");
- fprintf(outfilep, "\t.key_name_hint = \"%s\",\n", info->name_hint);
+ if (info->name_hint)
+ fprintf(outfilep, "\t.key_name_hint = \"%s\",\n", info->name_hint);
fprintf(outfilep, "\t.keyring = \"%s\",\n", info->keyring);
fprintf(outfilep, "\t.hash = %s_hash,\n", info->name_c);
fprintf(outfilep, "\t.hashlen = %u,\n", SHA256_DIGEST_LENGTH);
@@ -673,7 +674,8 @@ static int gen_key_rsa(EVP_PKEY *key, struct keyinfo *info)
if (!standalone) {
fprintf(outfilep, "\nstatic struct public_key %s_public_key = {\n", info->name_c);
fprintf(outfilep, "\t.type = PUBLIC_KEY_TYPE_RSA,\n");
- fprintf(outfilep, "\t.key_name_hint = \"%s\",\n", info->name_hint);
+ if (info->name_hint)
+ fprintf(outfilep, "\t.key_name_hint = \"%s\",\n", info->name_hint);
fprintf(outfilep, "\t.keyring = \"%s\",\n", info->keyring);
fprintf(outfilep, "\t.hash = %s_hash,\n", info->name_c);
fprintf(outfilep, "\t.hashlen = %u,\n", SHA256_DIGEST_LENGTH);
@@ -920,10 +922,6 @@ int main(int argc, char *argv[])
if (asprintf(&info->name_c, "key_%i", keys_idx + 1) < 0)
enomem_exit("asprintf");
- /* unfortunately, the fit name hint is mandatory in the barebox codebase */
- if (!info->name_hint)
- info->name_hint = info->name_c;
-
if (!info->keyring) {
info->keyring = strdup("fit");
fprintf(stderr, "Warning: No keyring provided in keyspec, defaulting to keyring=fit for %s\n", info->path);
--
2.47.3
^ permalink raw reply [flat|nested] 6+ messages in thread* [PATCH 3/4] public keys: rework keyrings as nested containers
2026-05-27 10:54 [PATCH 0/4] public keys: rework keyrings as nested containers Sascha Hauer
2026-05-27 10:54 ` [PATCH 1/4] public keys: make error message more informative Sascha Hauer
2026-05-27 10:54 ` [PATCH 2/4] public keys: make key_name_hint optional Sascha Hauer
@ 2026-05-27 10:54 ` Sascha Hauer
2026-05-27 10:54 ` [PATCH 4/4] public keys: allow keys to be members of multiple keyrings Sascha Hauer
2026-05-29 11:43 ` [PATCH 0/4] public keys: rework keyrings as nested containers Sascha Hauer
4 siblings, 0 replies; 6+ messages in thread
From: Sascha Hauer @ 2026-05-27 10:54 UTC (permalink / raw)
To: BAREBOX
Until now a keyring was just a string-valued field on each
struct public_key, and a key could only belong to a single keyring.
Switching policy at runtime required changing that string in place.
Introduce explicit struct keyring / struct keyring_link types: a
keyring is a named container of links, and each link references
either a public_key or a sub-keyring. Sub-keyring links are live
references, so adding a key to fit-devel after linking fit-devel
into fit makes the key visible via fit too.
Drop the global public_keys IDR; keyrings are tracked in a flat
name registry instead, and iteration goes through the keyring tree.
Change the "keys" command to print the current keyring hierarchy.
As a nice side effect separating the public keys entirely from the keyring
they are in allows us to put the compiled in keys into RO data.
Assisted-by: Claude Opus 4.7
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
commands/keys.c | 22 ++--
common/image-fit.c | 15 ++-
common/tlv/parser.c | 13 ++-
crypto/public-keys.c | 218 ++++++++++++++++++++++++++++++++++----
crypto/rsa.c | 3 +-
include/asm-generic/barebox.lds.h | 11 ++
include/crypto/public_key.h | 87 ++++++++++++---
scripts/keytoc.c | 24 +++--
8 files changed, 336 insertions(+), 57 deletions(-)
diff --git a/commands/keys.c b/commands/keys.c
index 12cb6ea2e3..06d289f18f 100644
--- a/commands/keys.c
+++ b/commands/keys.c
@@ -4,13 +4,23 @@
static int do_keys(int argc, char *argv[])
{
- const struct public_key *key;
- int id;
+ const struct keyring *kr;
+ const struct keyring_link *link;
- for_each_public_key(key, id) {
- printf("KEY: %*phN\tTYPE: %s\tKEYRING: %s\tHINT: %s\n", key->hashlen,
- key->hash, public_key_type_string(key->type), key->keyring,
- key->key_name_hint ?: "");
+ for_each_keyring(kr) {
+ printf("RING: %s\n", kr->name);
+ for_each_link_in_keyring(link, kr) {
+ if (link->type == KEYRING_LINK_KEY) {
+ const struct public_key *key = link->key;
+
+ printf(" KEY: %*phN\tTYPE: %s\tHINT: %s\n",
+ key->hashlen, key->hash,
+ public_key_type_string(key->type),
+ key->key_name_hint ?: "");
+ } else {
+ printf(" RING: %s\n", link->keyring->name);
+ }
+ }
}
return 0;
diff --git a/common/image-fit.c b/common/image-fit.c
index f5356be33d..b7daf433c3 100644
--- a/common/image-fit.c
+++ b/common/image-fit.c
@@ -253,10 +253,11 @@ static int fit_check_signature(struct fit_handle *handle, struct device_node *si
{
const char *fail_reason;
const struct public_key *key;
+ const struct keyring *fit_kr;
const char *key_name = NULL;
int sig_len;
const char *sig_value;
- int id, ret;
+ int ret;
sig_value = of_get_property(sig_node, "value", &sig_len);
if (!sig_value) {
@@ -266,10 +267,16 @@ static int fit_check_signature(struct fit_handle *handle, struct device_node *si
fail_reason = "no matching keys";
+ fit_kr = keyring_find("fit");
+ if (!fit_kr) {
+ pr_err("fit keyring not registered\n");
+ return -ENOKEY;
+ }
+
of_property_read_string(sig_node, "key-name-hint", &key_name);
if (key_name) {
- key = public_key_get(key_name, "fit");
- if (key) {
+ key = keyring_find_key(fit_kr, key_name);
+ if (!IS_ERR(key)) {
fail_reason = "verification failed";
ret = public_key_verify(key, sig_value, sig_len, hash, algo);
if (handle->verbose)
@@ -280,7 +287,7 @@ static int fit_check_signature(struct fit_handle *handle, struct device_node *si
}
}
- for_each_public_key_keyring(key, id, "fit") {
+ for_each_key_in_keyring(key, fit_kr) {
/* Don't recheck with same key as before */
if (key_name && streq_ptr(key->key_name_hint, key_name))
diff --git a/common/tlv/parser.c b/common/tlv/parser.c
index 4c0b6b5c6f..d61dfa2c47 100644
--- a/common/tlv/parser.c
+++ b/common/tlv/parser.c
@@ -49,15 +49,16 @@ static int tlv_verify_try_key(const struct public_key *key, const uint8_t *sig,
return ret;
}
-static int tlv_verify(const struct tlv_header *header, const char *keyring)
+static int tlv_verify(const struct tlv_header *header, const char *keyring_name)
{
const struct public_key *key;
+ const struct keyring *kr;
size_t payload_sz = tlv_spki_hash_offset(header);
const void *spki_tlv_ptr = (void *)header + payload_sz;
u32 spki_tlv = get_unaligned_le32(spki_tlv_ptr);
int SPKI_LEN = 4;
u32 sig_len = get_unaligned_be16(&header->length_sig);
- int ret, id;
+ int ret;
int count_spki_matches = 0;
if (!IS_ENABLED(CONFIG_TLV_SIGNATURE)) {
@@ -68,7 +69,13 @@ static int tlv_verify(const struct tlv_header *header, const char *keyring)
return -EPROTO;
}
- for_each_public_key_keyring(key, id, keyring) {
+ kr = keyring_find(keyring_name);
+ if (!kr) {
+ pr_warn("TLV keyring %s not found\n", keyring_name);
+ return -ENOKEY;
+ }
+
+ for_each_key_in_keyring(key, kr) {
u32 spki_key = get_unaligned_le32(key->hash);
if (spki_key == spki_tlv) {
diff --git a/crypto/public-keys.c b/crypto/public-keys.c
index 2b4bac55b7..84bc03e4e3 100644
--- a/crypto/public-keys.c
+++ b/crypto/public-keys.c
@@ -5,38 +5,216 @@
#include <crypto/public_key.h>
#include <crypto/rsa.h>
#include <crypto/ecdsa.h>
+#include <linux/list.h>
+#include <malloc.h>
+#include <xfuncs.h>
-DEFINE_IDR(public_keys);
+LIST_HEAD(keyring_registry);
-const struct public_key *public_key_get(const char *name, const char *keyring)
+struct keyring *keyring_find(const char *name)
+{
+ struct keyring *kr;
+
+ if (!name)
+ return NULL;
+
+ list_for_each_entry(kr, &keyring_registry, node) {
+ if (!strcmp(kr->name, name))
+ return kr;
+ }
+ return NULL;
+}
+
+struct keyring *keyring_create(const char *name)
+{
+ struct keyring *kr;
+
+ if (!name || !*name)
+ return ERR_PTR(-EINVAL);
+
+ if (keyring_find(name))
+ return ERR_PTR(-EEXIST);
+
+ kr = xzalloc(sizeof(*kr));
+ kr->name = xstrdup(name);
+ INIT_LIST_HEAD(&kr->links);
+ INIT_LIST_HEAD(&kr->node);
+ list_add_tail(&kr->node, &keyring_registry);
+
+ return kr;
+}
+
+int keyring_link_key(struct keyring *kr, const struct public_key *key)
+{
+ struct keyring_link *link;
+
+ if (!kr || !key)
+ return -EINVAL;
+
+ link = xzalloc(sizeof(*link));
+ link->type = KEYRING_LINK_KEY;
+ link->key = key;
+ list_add_tail(&link->node, &kr->links);
+
+ return 0;
+}
+
+int keyring_unlink_key(struct keyring *kr, const struct public_key *key)
+{
+ struct keyring_link *link, *n;
+
+ if (!kr || !key)
+ return -EINVAL;
+
+ list_for_each_entry_safe(link, n, &kr->links, node) {
+ if (link->type == KEYRING_LINK_KEY && link->key == key) {
+ list_del(&link->node);
+ free(link);
+ return 0;
+ }
+ }
+ return -ENOENT;
+}
+
+static bool keyring_contains(const struct keyring *kr, const struct keyring *target)
+{
+ const struct keyring_link *link;
+
+ if (kr == target)
+ return true;
+
+ list_for_each_entry(link, &kr->links, node) {
+ if (link->type != KEYRING_LINK_KEYRING)
+ continue;
+ if (keyring_contains(link->keyring, target))
+ return true;
+ }
+ return false;
+}
+
+int keyring_link_keyring(struct keyring *kr, const struct keyring *sub)
+{
+ struct keyring_link *link;
+
+ if (!kr || !sub)
+ return -EINVAL;
+
+ /*
+ * Refuse to create a cycle: if sub already (transitively) contains kr
+ * — or sub is kr itself — adding sub as a child of kr would loop.
+ * Assuming the graph was cycle-free before, this check is enough to
+ * keep it that way.
+ */
+ if (keyring_contains(sub, kr))
+ return -ELOOP;
+
+ link = xzalloc(sizeof(*link));
+ link->type = KEYRING_LINK_KEYRING;
+ link->keyring = sub;
+ list_add_tail(&link->node, &kr->links);
+
+ return 0;
+}
+
+int keyring_unlink_keyring(struct keyring *kr, const struct keyring *sub)
+{
+ struct keyring_link *link, *n;
+
+ if (!kr || !sub)
+ return -EINVAL;
+
+ list_for_each_entry_safe(link, n, &kr->links, node) {
+ if (link->type == KEYRING_LINK_KEYRING && link->keyring == sub) {
+ list_del(&link->node);
+ free(link);
+ return 0;
+ }
+ }
+ return -ENOENT;
+}
+
+const struct public_key *keyring_iter_next(struct keyring_iter *it,
+ const struct keyring *root)
+{
+ if (it->depth < 0) {
+ if (!root)
+ return NULL;
+ it->depth = 0;
+ it->stack[0] = root;
+ it->cursor[0] = (struct list_head *)&root->links;
+ }
+
+ while (it->depth >= 0) {
+ struct list_head *head = (struct list_head *)&it->stack[it->depth]->links;
+ struct list_head *next = it->cursor[it->depth]->next;
+ struct keyring_link *link;
+
+ if (next == head) {
+ it->depth--;
+ continue;
+ }
+
+ it->cursor[it->depth] = next;
+ link = list_entry(next, struct keyring_link, node);
+
+ if (link->type == KEYRING_LINK_KEY)
+ return link->key;
+
+ if (it->depth + 1 >= KEYRING_MAX_DEPTH) {
+ pr_warn("keyring nesting too deep, skipping %s\n",
+ link->keyring->name);
+ continue;
+ }
+
+ it->depth++;
+ it->stack[it->depth] = link->keyring;
+ it->cursor[it->depth] = (struct list_head *)&link->keyring->links;
+ }
+
+ return NULL;
+}
+
+const struct public_key *keyring_find_key(const struct keyring *kr,
+ const char *key_name_hint)
{
const struct public_key *key;
- int id;
- for_each_public_key_keyring(key, id, keyring) {
+ if (!kr || !key_name_hint)
+ return ERR_PTR(-EINVAL);
+
+ for_each_key_in_keyring(key, kr) {
if (!key->key_name_hint)
continue;
- if (!strcmp(key->key_name_hint, name))
+ if (!strcmp(key->key_name_hint, key_name_hint))
return key;
}
- return NULL;
+ return ERR_PTR(-ENOENT);
}
-int public_key_add(struct public_key *key)
+int public_key_add(const char *keyring, const struct public_key *key)
{
- if (!key->keyring || *key->keyring == '\0') {
- pr_warn("Aborting addition of public key: No keyring specified\n");
+ struct keyring *kr;
+ const struct public_key *conflict;
+
+ if (!keyring || !*keyring)
return -EINVAL;
+
+ kr = keyring_find(keyring);
+ if (!kr) {
+ kr = keyring_create(keyring);
+ if (IS_ERR(kr))
+ return PTR_ERR(kr);
}
- if (public_key_get(key->key_name_hint, key->keyring)) {
+ conflict = keyring_find_key(kr, key->key_name_hint);
+ if (!IS_ERR(conflict)) {
pr_warn("Cannot add key: key_name_hint %s already exists in keyring %s\n",
- key->key_name_hint, key->keyring);
+ key->key_name_hint, keyring);
return -EEXIST;
}
- return idr_alloc(&public_keys, key, 0, INT_MAX, GFP_NOWAIT);
+ return keyring_link_key(kr, key);
}
int public_key_verify(const struct public_key *key, const uint8_t *sig,
@@ -53,18 +231,20 @@ int public_key_verify(const struct public_key *key, const uint8_t *sig,
return -ENOKEY;
}
-extern struct public_key * __public_keys_start[];
-extern struct public_key * __public_keys_end[];
+extern const struct public_key_record __public_keys_start[];
+extern const struct public_key_record __public_keys_end[];
static int init_public_keys(void)
{
- struct public_key * const *iter;
+ const struct public_key_record *rec;
int ret;
- for (iter = __public_keys_start; iter != __public_keys_end; iter++) {
- ret = idr_alloc(&public_keys, *iter, 0, INT_MAX, GFP_NOWAIT);
- if (ret < 0)
- pr_warn("error while adding key\n");
+ for (rec = __public_keys_start; rec != __public_keys_end; rec++) {
+ ret = public_key_add(rec->keyring, rec->key);
+ if (ret)
+ pr_warn("error while adding key %s to %s: %pe\n",
+ rec->key->key_name_hint ?: "(noname)",
+ rec->keyring, ERR_PTR(ret));
}
return 0;
diff --git a/crypto/rsa.c b/crypto/rsa.c
index 0e752f11b4..0b20b59477 100644
--- a/crypto/rsa.c
+++ b/crypto/rsa.c
@@ -468,8 +468,7 @@ static void rsa_init_keys_of(void)
continue;
}
- key->keyring = "fit";
- ret = public_key_add(key);
+ ret = public_key_add("fit", key);
if (ret)
pr_err("Cannot add rsa key %s: %pe\n",
key->key_name_hint, ERR_PTR(ret));
diff --git a/include/asm-generic/barebox.lds.h b/include/asm-generic/barebox.lds.h
index 12082d567a..008217e808 100644
--- a/include/asm-generic/barebox.lds.h
+++ b/include/asm-generic/barebox.lds.h
@@ -129,7 +129,18 @@
#endif
+/*
+ * GCC emits "const data with absolute relocations" into .data.rel.ro*
+ * (e.g. a `static const struct` that contains pointers to other symbols).
+ * In a statically linked image these are fully resolved at link time and
+ * never written at runtime, so place them with .rodata instead of letting
+ * them fall through into the writable .data section.
+ */
+#define BAREBOX_RELRO_DATA \
+ *(.data.rel.ro .data.rel.ro.*)
+
#define RO_DATA_SECTION \
+ BAREBOX_RELRO_DATA \
BAREBOX_INITCALLS \
BAREBOX_EXITCALLS \
BAREBOX_CMDS \
diff --git a/include/crypto/public_key.h b/include/crypto/public_key.h
index 0fd8e1e304..3866b13a75 100644
--- a/include/crypto/public_key.h
+++ b/include/crypto/public_key.h
@@ -2,7 +2,7 @@
#define __CRYPTO_PUBLIC_KEY_H
#include <digest.h>
-#include <linux/idr.h>
+#include <linux/list.h>
#include <string.h>
struct rsa_public_key;
@@ -28,7 +28,6 @@ static inline const char *public_key_type_string(enum public_key_type type)
struct public_key {
enum public_key_type type;
const char *key_name_hint;
- char *keyring;
const unsigned char *hash;
unsigned int hashlen;
@@ -38,21 +37,83 @@ struct public_key {
};
};
-int public_key_add(struct public_key *key);
-const struct public_key *public_key_get(const char *name, const char *keyring);
-const struct public_key *public_key_next(const struct public_key *prev);
+/*
+ * Linker-list entry assigning a compiled-in public_key to a named keyring.
+ * Emitted by scripts/keytoc into .public_keys.rodata.* and consumed at init.
+ */
+struct public_key_record {
+ const char *keyring;
+ const struct public_key *key;
+};
+
+/*
+ * A keyring is a named container that holds keyring_link entries. Each link
+ * references either a public_key or another keyring (sub-keyring), so keyrings
+ * can be nested. A given key may be linked into any number of keyrings.
+ */
+struct keyring {
+ const char *name;
+ struct list_head links; /* list of struct keyring_link */
+ struct list_head node; /* link in keyring_registry */
+};
+
+enum keyring_link_type {
+ KEYRING_LINK_KEY,
+ KEYRING_LINK_KEYRING,
+};
+
+struct keyring_link {
+ enum keyring_link_type type;
+ struct list_head node;
+ union {
+ const struct public_key *key;
+ const struct keyring *keyring;
+ };
+};
+
+struct keyring *keyring_create(const char *name);
+struct keyring *keyring_find(const char *name);
+
+int keyring_link_key(struct keyring *kr, const struct public_key *key);
+int keyring_unlink_key(struct keyring *kr, const struct public_key *key);
+
+int keyring_link_keyring(struct keyring *kr, const struct keyring *sub);
+int keyring_unlink_keyring(struct keyring *kr, const struct keyring *sub);
+
+const struct public_key *keyring_find_key(const struct keyring *kr,
+ const char *key_name_hint);
+
+#define KEYRING_MAX_DEPTH 4
+
+struct keyring_iter {
+ const struct keyring *stack[KEYRING_MAX_DEPTH];
+ struct list_head *cursor[KEYRING_MAX_DEPTH];
+ int depth;
+};
+
+const struct public_key *keyring_iter_next(struct keyring_iter *it,
+ const struct keyring *root);
+
+/*
+ * Iterate every key reachable from @kr, recursing into sub-keyrings.
+ * The same key may be yielded more than once if it is linked into multiple
+ * (sub-)keyrings; callers that care must dedup themselves.
+ */
+#define for_each_key_in_keyring(key, kr) \
+ for (struct keyring_iter __it = { .depth = -1 }; \
+ ((key) = keyring_iter_next(&__it, (kr))) != NULL; \
+ )
-extern struct idr public_keys;
+/* Iterate only the direct links of @kr (does not recurse into sub-keyrings) */
+#define for_each_link_in_keyring(link, kr) \
+ list_for_each_entry(link, &(kr)->links, node)
-#define for_each_public_key(key, id) \
- idr_for_each_entry(&public_keys, key, id)
+extern struct list_head keyring_registry;
-#define for_each_public_key_keyring(key, id, _keyring) \
- for_each_public_key(key, id) \
- if (!key->keyring || strcmp(key->keyring, _keyring) != 0) \
- continue; \
- else
+#define for_each_keyring(kr) \
+ list_for_each_entry(kr, &keyring_registry, node)
+int public_key_add(const char *keyring, const struct public_key *key);
int public_key_verify(const struct public_key *key, const uint8_t *sig,
const uint32_t sig_len, const uint8_t *hash,
enum hash_algo algo);
diff --git a/scripts/keytoc.c b/scripts/keytoc.c
index 40601827b7..7e910422a7 100644
--- a/scripts/keytoc.c
+++ b/scripts/keytoc.c
@@ -535,7 +535,7 @@ static int gen_key_ecdsa(EVP_PKEY *key, struct keyinfo *info)
fprintf(stderr, "ERROR: generating a dts snippet for ECDSA keys is not yet supported\n");
return -EOPNOTSUPP;
} else {
- fprintf(outfilep, "\nstatic unsigned char %s_hash[] = {\n\t", info->name_c);
+ fprintf(outfilep, "\nstatic const unsigned char %s_hash[] = {\n\t", info->name_c);
ret = print_hash(key);
if (ret)
@@ -557,24 +557,26 @@ static int gen_key_ecdsa(EVP_PKEY *key, struct keyinfo *info)
fprintf(outfilep, "\n};\n\n");
- fprintf(outfilep, "static struct ecdsa_public_key %s = {\n", info->name_c);
+ fprintf(outfilep, "static const struct ecdsa_public_key %s = {\n", info->name_c);
fprintf(outfilep, "\t.curve_name = \"%s\",\n", group);
fprintf(outfilep, "\t.x = %s_x,\n", info->name_c);
fprintf(outfilep, "\t.y = %s_y,\n", info->name_c);
fprintf(outfilep, "};\n");
if (!standalone) {
- fprintf(outfilep, "\nstatic struct public_key %s_public_key = {\n", info->name_c);
+ fprintf(outfilep, "\nstatic const struct public_key %s_public_key = {\n", info->name_c);
fprintf(outfilep, "\t.type = PUBLIC_KEY_TYPE_ECDSA,\n");
if (info->name_hint)
fprintf(outfilep, "\t.key_name_hint = \"%s\",\n", info->name_hint);
- fprintf(outfilep, "\t.keyring = \"%s\",\n", info->keyring);
fprintf(outfilep, "\t.hash = %s_hash,\n", info->name_c);
fprintf(outfilep, "\t.hashlen = %u,\n", SHA256_DIGEST_LENGTH);
fprintf(outfilep, "\t.ecdsa = &%s,\n", info->name_c);
fprintf(outfilep, "};\n");
fprintf(outfilep, "\n");
- fprintf(outfilep, "const struct public_key *__%s_public_key __ll_elem(.public_keys.rodata.%s) = &%s_public_key;\n", info->name_c, info->name_c, info->name_c);
+ fprintf(outfilep, "static const struct public_key_record __%s_public_key __ll_elem(.public_keys.rodata.%s) = {\n", info->name_c, info->name_c);
+ fprintf(outfilep, "\t.keyring = \"%s\",\n", info->keyring);
+ fprintf(outfilep, "\t.key = &%s_public_key,\n", info->name_c);
+ fprintf(outfilep, "};\n");
}
}
@@ -635,7 +637,7 @@ static int gen_key_rsa(EVP_PKEY *key, struct keyinfo *info)
fprintf(outfilep, "\t\t\tkey-name-hint = \"%s\";\n", info->name_c);
fprintf(outfilep, "\t\t};\n");
} else {
- fprintf(outfilep, "\nstatic unsigned char %s_hash[] = {\n\t", info->name_c);
+ fprintf(outfilep, "\nstatic const unsigned char %s_hash[] = {\n\t", info->name_c);
ret = print_hash(key);
if (ret)
@@ -661,7 +663,7 @@ static int gen_key_rsa(EVP_PKEY *key, struct keyinfo *info)
fprintf(outfilep, "struct rsa_public_key __key_%s;\n", info->name_c);
fprintf(outfilep, "struct rsa_public_key __key_%s = {\n", info->name_c);
} else {
- fprintf(outfilep, "static struct rsa_public_key %s = {\n", info->name_c);
+ fprintf(outfilep, "static const struct rsa_public_key %s = {\n", info->name_c);
}
fprintf(outfilep, "\t.len = %d,\n", bits / 32);
@@ -672,17 +674,19 @@ static int gen_key_rsa(EVP_PKEY *key, struct keyinfo *info)
fprintf(outfilep, "};\n");
if (!standalone) {
- fprintf(outfilep, "\nstatic struct public_key %s_public_key = {\n", info->name_c);
+ fprintf(outfilep, "\nstatic const struct public_key %s_public_key = {\n", info->name_c);
fprintf(outfilep, "\t.type = PUBLIC_KEY_TYPE_RSA,\n");
if (info->name_hint)
fprintf(outfilep, "\t.key_name_hint = \"%s\",\n", info->name_hint);
- fprintf(outfilep, "\t.keyring = \"%s\",\n", info->keyring);
fprintf(outfilep, "\t.hash = %s_hash,\n", info->name_c);
fprintf(outfilep, "\t.hashlen = %u,\n", SHA256_DIGEST_LENGTH);
fprintf(outfilep, "\t.rsa = &%s,\n", info->name_c);
fprintf(outfilep, "};\n");
fprintf(outfilep, "\n");
- fprintf(outfilep, "const struct public_key *__%s_public_key __ll_elem(.public_keys.rodata.%s) = &%s_public_key;\n", info->name_c, info->name_c, info->name_c);
+ fprintf(outfilep, "static const struct public_key_record __%s_public_key __ll_elem(.public_keys.rodata.%s) = {\n", info->name_c, info->name_c);
+ fprintf(outfilep, "\t.keyring = \"%s\",\n", info->keyring);
+ fprintf(outfilep, "\t.key = &%s_public_key,\n", info->name_c);
+ fprintf(outfilep, "};\n");
}
}
--
2.47.3
^ permalink raw reply [flat|nested] 6+ messages in thread* [PATCH 4/4] public keys: allow keys to be members of multiple keyrings
2026-05-27 10:54 [PATCH 0/4] public keys: rework keyrings as nested containers Sascha Hauer
` (2 preceding siblings ...)
2026-05-27 10:54 ` [PATCH 3/4] public keys: rework keyrings as nested containers Sascha Hauer
@ 2026-05-27 10:54 ` Sascha Hauer
2026-05-29 11:43 ` [PATCH 0/4] public keys: rework keyrings as nested containers Sascha Hauer
4 siblings, 0 replies; 6+ messages in thread
From: Sascha Hauer @ 2026-05-27 10:54 UTC (permalink / raw)
To: BAREBOX
The same signing identity can be valid in more than one context — e.g.
the development PEM is used both as a FIT image signer and as a TLV
signer. Until now keytoc forced one keyring per key, so the Kconfig
default listed the snakeoil PEM twice (once per target keyring) and
emitted two full copies of the modulus / rr / hash / public_key struct
into rodata.
Let the keyring= option in a keyspec appear more than once. Each
occurrence appends to a list; the emitted key data (struct public_key,
rsa/ecdsa_public_key, hash) is unchanged and singular, and one
struct public_key_record is emitted per keyring all pointing back at
the same key. Symbol/section names are uniquified with a per-key
record index (key_N_record_M).
For the development-key default, collapse the two CRYPTO_PUBLIC_KEYS
entries into one (keyring=fit,keyring=tlv-generic,fit-hint=...). With
4096-bit RSA this avoids re-emitting ~1 KB per shared identity into
rodata.
While here, have the keys command print a keyring header so the now
N-records-per-key output is readable.
Assisted-by: Claude Opus 4.7
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
crypto/Makefile | 6 ++----
scripts/keytoc.c | 61 ++++++++++++++++++++++++++++++++++++++++----------------
2 files changed, 46 insertions(+), 21 deletions(-)
diff --git a/crypto/Makefile b/crypto/Makefile
index 17043316c4..0c4a900504 100644
--- a/crypto/Makefile
+++ b/crypto/Makefile
@@ -33,12 +33,10 @@ CONFIG_CRYPTO_PUBLIC_KEYS := $(foreach d,$(CONFIG_CRYPTO_PUBLIC_KEYS),"$(d)")
ifdef CONFIG_CRYPTO_BUILTIN_DEVELOPMENT_KEYS
ifdef CONFIG_CRYPTO_RSA
-CONFIG_CRYPTO_PUBLIC_KEYS += keyring=fit,fit-hint=rsa-devel:$(srctree)/crypto/snakeoil-4096-development.pem
-CONFIG_CRYPTO_PUBLIC_KEYS += keyring=tlv-generic:$(srctree)/crypto/snakeoil-4096-development.pem
+CONFIG_CRYPTO_PUBLIC_KEYS += keyring=fit,keyring=tlv-generic,fit-hint=rsa-devel:$(srctree)/crypto/snakeoil-4096-development.pem
endif
ifdef CONFIG_CRYPTO_ECDSA
-CONFIG_CRYPTO_PUBLIC_KEYS += keyring=fit,fit-hint=ecdsa-devel:$(srctree)/crypto/snakeoil-ecdsa-development.pem
-CONFIG_CRYPTO_PUBLIC_KEYS += keyring=tlv-generic:$(srctree)/crypto/snakeoil-ecdsa-development.pem
+CONFIG_CRYPTO_PUBLIC_KEYS += keyring=fit,keyring=tlv-generic,fit-hint=ecdsa-devel:$(srctree)/crypto/snakeoil-ecdsa-development.pem
endif
endif
diff --git a/scripts/keytoc.c b/scripts/keytoc.c
index 7e910422a7..e78d010481 100644
--- a/scripts/keytoc.c
+++ b/scripts/keytoc.c
@@ -34,7 +34,8 @@
struct keyinfo {
char *spec;
char *name_hint;
- char *keyring;
+ char **keyrings;
+ int nr_keyrings;
char *path;
char *name_c;
};
@@ -564,6 +565,8 @@ static int gen_key_ecdsa(EVP_PKEY *key, struct keyinfo *info)
fprintf(outfilep, "\t.y = %s_y,\n", info->name_c);
fprintf(outfilep, "};\n");
if (!standalone) {
+ int i;
+
fprintf(outfilep, "\nstatic const struct public_key %s_public_key = {\n", info->name_c);
fprintf(outfilep, "\t.type = PUBLIC_KEY_TYPE_ECDSA,\n");
if (info->name_hint)
@@ -572,11 +575,14 @@ static int gen_key_ecdsa(EVP_PKEY *key, struct keyinfo *info)
fprintf(outfilep, "\t.hashlen = %u,\n", SHA256_DIGEST_LENGTH);
fprintf(outfilep, "\t.ecdsa = &%s,\n", info->name_c);
fprintf(outfilep, "};\n");
- fprintf(outfilep, "\n");
- fprintf(outfilep, "static const struct public_key_record __%s_public_key __ll_elem(.public_keys.rodata.%s) = {\n", info->name_c, info->name_c);
- fprintf(outfilep, "\t.keyring = \"%s\",\n", info->keyring);
- fprintf(outfilep, "\t.key = &%s_public_key,\n", info->name_c);
- fprintf(outfilep, "};\n");
+ for (i = 0; i < info->nr_keyrings; i++) {
+ fprintf(outfilep, "\n");
+ fprintf(outfilep, "static const struct public_key_record __%s_record_%d __ll_elem(.public_keys.rodata.%s_record_%d) = {\n",
+ info->name_c, i, info->name_c, i);
+ fprintf(outfilep, "\t.keyring = \"%s\",\n", info->keyrings[i]);
+ fprintf(outfilep, "\t.key = &%s_public_key,\n", info->name_c);
+ fprintf(outfilep, "};\n");
+ }
}
}
@@ -674,6 +680,8 @@ static int gen_key_rsa(EVP_PKEY *key, struct keyinfo *info)
fprintf(outfilep, "};\n");
if (!standalone) {
+ int i;
+
fprintf(outfilep, "\nstatic const struct public_key %s_public_key = {\n", info->name_c);
fprintf(outfilep, "\t.type = PUBLIC_KEY_TYPE_RSA,\n");
if (info->name_hint)
@@ -682,11 +690,14 @@ static int gen_key_rsa(EVP_PKEY *key, struct keyinfo *info)
fprintf(outfilep, "\t.hashlen = %u,\n", SHA256_DIGEST_LENGTH);
fprintf(outfilep, "\t.rsa = &%s,\n", info->name_c);
fprintf(outfilep, "};\n");
- fprintf(outfilep, "\n");
- fprintf(outfilep, "static const struct public_key_record __%s_public_key __ll_elem(.public_keys.rodata.%s) = {\n", info->name_c, info->name_c);
- fprintf(outfilep, "\t.keyring = \"%s\",\n", info->keyring);
- fprintf(outfilep, "\t.key = &%s_public_key,\n", info->name_c);
- fprintf(outfilep, "};\n");
+ for (i = 0; i < info->nr_keyrings; i++) {
+ fprintf(outfilep, "\n");
+ fprintf(outfilep, "static const struct public_key_record __%s_record_%d __ll_elem(.public_keys.rodata.%s_record_%d) = {\n",
+ info->name_c, i, info->name_c, i);
+ fprintf(outfilep, "\t.keyring = \"%s\",\n", info->keyrings[i]);
+ fprintf(outfilep, "\t.key = &%s_public_key,\n", info->name_c);
+ fprintf(outfilep, "};\n");
+ }
}
}
@@ -775,9 +786,21 @@ static bool parse_info(char *p, struct keyinfo *out)
v = strdup(v);
if (!v)
enomem_exit(__func__);
- if (strcmp(k, "keyring") == 0)
- out->keyring = strdup(v);
- else if (strcmp(k, "fit-hint") == 0)
+ if (strcmp(k, "keyring") == 0) {
+ int i;
+ for (i = 0; i < out->nr_keyrings; i++) {
+ if (strcmp(out->keyrings[i], v) == 0) {
+ fprintf(stderr,
+ "duplicate keyring=%s in keyspec\n", v);
+ return false;
+ }
+ }
+ out->keyrings = realloc(out->keyrings,
+ (out->nr_keyrings + 1) * sizeof(char *));
+ if (!out->keyrings)
+ enomem_exit(__func__);
+ out->keyrings[out->nr_keyrings++] = strdup(v);
+ } else if (strcmp(k, "fit-hint") == 0)
out->name_hint = strdup(v);
else
return false;
@@ -862,7 +885,7 @@ int main(int argc, char *argv[])
}
if (optind == argc) {
- fprintf(stderr, "Usage: %s [-ods] keyring=<keyring>[,fit-hint=<hint>]:<crt> ...\n", argv[0]);
+ fprintf(stderr, "Usage: %s [-ods] keyring=<keyring>[,keyring=<keyring>...][,fit-hint=<hint>]:<crt> ...\n", argv[0]);
fprintf(stderr, "\t-o FILE\twrite output into FILE instead of stdout\n");
fprintf(stderr, "\t-d\tgenerate device tree snippet instead of C code\n");
fprintf(stderr, "\t-s\tgenerate standalone key outside FIT image keyring\n");
@@ -926,8 +949,12 @@ int main(int argc, char *argv[])
if (asprintf(&info->name_c, "key_%i", keys_idx + 1) < 0)
enomem_exit("asprintf");
- if (!info->keyring) {
- info->keyring = strdup("fit");
+ if (info->nr_keyrings == 0) {
+ info->keyrings = malloc(sizeof(char *));
+ if (!info->keyrings)
+ enomem_exit(__func__);
+ info->keyrings[0] = strdup("fit");
+ info->nr_keyrings = 1;
fprintf(stderr, "Warning: No keyring provided in keyspec, defaulting to keyring=fit for %s\n", info->path);
}
}
--
2.47.3
^ permalink raw reply [flat|nested] 6+ messages in thread