public keys: rework keyrings as nested containers
cover public keys: rework keyrings as nested containers
So far a public key had a char *keyring meaning that a key can only be part of a single keyring. This series overcomes this, with it keys can be contained in multiple keyrings and keyrings can be part of other keyrings. To get there a new struct keyring_link is introduced which is instanciated per link and links a key/keyring to a keyring. The keys command is updated to reflect that, so an example output could be:
RING: fit
KEY: 9160ff3ab26e1842d1fbfaca48befce2664f57245048856e779531bb260cbc20 TYPE: ECDSA HINT:
RING: snakeoil
RING: fit-devel
RING: tlv-generic
RING: snakeoil
RING: snakeoil
KEY: 4f48c67c9a28be596931de5b137f8207f15a505d1b7a742731014dbcd4c799d9 TYPE: ECDSA HINT: ecdsa-devel
KEY: 7fbcb3b2ddfab044ef874550c7cbd1b357afeed55fb6f48be9c22b2cbef72856 TYPE: RSA HINT: rsa-devel
RING: fit-devel
KEY: 7fbcb3b2ddfab044ef874550c7cbd1b357afeed55fb6f48be9c22b2cbef72856 TYPE: RSA HINT:
RING: tlv-feature
KEY: 54b7b9c040cd81dd1e495832b8a2724a8f48e75f90fcd982631695669a805dd0 TYPE: RSA HINT:
RING: tlv-base
KEY: 2f908960b1d74b3a8d0076376870020f8d6504ebb7a1c68c1c383ac8140690e5 TYPE: RSA HINT:The FIT image authentication code still uses the "fit" keyring as before, but now we can link new keys to the keyring during runtime as needed. Furthermore a key can be used for multiple purposes without duplicating it.
As a nice side effect the keys themselves become readonly and the compiled in keys can be moved to the RO data segment.
---
Sascha Hauer (4):
public keys: make error message more informative public keys: make key_name_hint optional
public keys: rework keyrings as nested containers public keys: allow keys to be members of multiple keyrings
commands/keys.c | 21 +++- common/image-fit.c | 15 ++- common/tlv/parser.c | 13 ++- crypto/Makefile | 6 +- crypto/public-keys.c | 221 ++++++++++++++++++++++++++++++++++---- crypto/rsa.c | 3 +- include/asm-generic/barebox.lds.h | 11 ++ include/crypto/public_key.h | 87 ++++++++++++--- scripts/keytoc.c | 79 +++++++++----- 9 files changed, 381 insertions(+), 75 deletions(-)
---
base-commit: 19ce0bdebf4856cfe6d0610a6e4d8d917d658ec3
change-id: 20260522-public-keys-ba609c65c114
Best regards,
Signature · 1 line
Sascha Hauer <[email protected]>
PATCH 1/4 public keys: make error message more informative
When adding a key to a keyring fails because a key with the same name hint already exists, print which keyring and which hint is duplicate. This also removes the string "fit" from the error message which seems to be a remnant of days when only FIT images used keys.
--- crypto/public-keys.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
crypto/public-keys.c +2 -1
@@ -29,7 +29,8 @@ int public_key_add(struct public_key *key) }if (public_key_get (key->key_name_hint, key->keyring)) { -pr_warn ("Aborting addition of public key: Duplicate fit name hint\n" ); +pr_warn ("Cannot add key: key_name_hint %s already exists in keyring %s\n" , + key->key_name_hint, key->keyring);return -EEXIST ; }
Signature · 1 line
2.47.3
PATCH 2/4 public keys: make key_name_hint optional
In reply to Sascha HauerThe 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.
--- commands/keys.c | 3 ++- crypto/public-keys.c | 2 ++ scripts/keytoc.c | 10 ++++------ 3 files changed, 8 insertions(+), 7 deletions(-)
commands/keys.c +2 -1
@@ -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 ;
crypto/public-keys.c +2 -0
@@ -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; }
scripts/keytoc.c +4 -6
@@ -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);
Signature · 1 line
2.47.3
PATCH 3/4 public keys: rework keyrings as nested containers
In reply to Sascha HauerUntil 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.
--- 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(-)
commands/keys.c +16 -6
@@ -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 ;
common/image-fit.c +11 -4
@@ -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))
common/tlv/parser.c +10 -3
@@ -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) {
crypto/public-keys.c +199 -19
255 lines not on this page · raw message
crypto/rsa.c +1 -2
@@ -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));
include/asm-generic/barebox.lds.h +11 -0
@@ -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 \
include/crypto/public_key.h +74 -13
109 lines not on this page · raw message
scripts/keytoc.c +14 -10
@@ -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" ); } }
Signature · 1 line
2.47.3
PATCH 4/4 public keys: allow keys to be members of multiple keyrings
In reply to Sascha HauerThe 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.
--- crypto/Makefile | 6 ++---- scripts/keytoc.c | 61 ++++++++++++++++++++++++++++++++++++++++---------------- 2 files changed, 46 insertions(+), 21 deletions(-)
crypto/Makefile +2 -4
@@ -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
scripts/keytoc.c +44 -17
109 lines not on this page · raw message
Signature · 1 line
2.47.3
Sascha Hauer
AppliedQuoted 9 lines · Sascha Hauer
So far a public key had a char *keyring meaning that a key can only be part of a single keyring. This series overcomes this, with it keys can be contained in multiple keyrings and keyrings can be part of other keyrings. To get there a new struct
keyring_linkis introduced which is instanciated per link and links a key/keyring to a keyring. The keys command is updated to reflect that, so an example output could be:[...]
Applied, thanks!
[1/4] public keys: make error message more informative
https://git.pengutronix.de/cgit/barebox/commit/?id=7c1e2e1a75e2 (link may not be stable)
[2/4] public keys: make key_name_hint optional
https://git.pengutronix.de/cgit/barebox/commit/?id=7d7cd5b01e33 (link may not be stable)
[3/4] public keys: rework keyrings as nested containers
https://git.pengutronix.de/cgit/barebox/commit/?id=499d868a876e (link may not be stable)
[4/4] public keys: allow keys to be members of multiple keyrings
https://git.pengutronix.de/cgit/barebox/commit/?id=fac191dd0c97 (link may not be stable)
Best regards,
Signature · 1 line
Sascha Hauer <[email protected]>