mail archive of the barebox mailing list
 help / color / mirror / Atom feed
From: Jonas Rebmann <jre@pengutronix.de>
To: Sascha Hauer <s.hauer@pengutronix.de>,
	 BAREBOX <barebox@lists.infradead.org>
Cc: Ahmad Fatoum <a.fatoum@pengutronix.de>,
	 Jonas Rebmann <jre@pengutronix.de>
Subject: [PATCH v2 03/17] crypto: Add support for keyrings
Date: Tue, 28 Oct 2025 19:03:08 +0100	[thread overview]
Message-ID: <20251028-tlv-signature-v2-3-3bafce636ad7@pengutronix.de> (raw)
In-Reply-To: <20251028-tlv-signature-v2-0-3bafce636ad7@pengutronix.de>

Public keys for TLV verification will be handled with the mechanism used
in the existing fitimage verification where scripts/keytoc.c generates
public_keys.h based on CONFIG_CRYPTO_PUBLIC_KEYS.

Expand the existing keyspec parser to support a new syntax of
  keyring=<keyring>[,fit-hint=<fit_hint>]:<crt>
in addition to the legacy syntax of
  [<fit_hint>:]<crt>

With the new keyspec parsing, keyring name and fit hint need to confirm
to the regular expression
  [a-zA-Z][a-zA-Z0-9_-]*

For backwards compatibility, the old syntax stays supported with the
keyring defaulting to "fit" and a warning emitted.

Note however that the restriction of fit name hints to above regex
applies even when supplied in the legacy syntax.

To be able to error out when encountering an invalid keyspec, some
refactoring of keytoc.c was necessary.

Signed-off-by: Jonas Rebmann <jre@pengutronix.de>
---
 crypto/Kconfig              |  37 +++++----
 include/crypto/public_key.h |   1 +
 scripts/keytoc.c            | 186 +++++++++++++++++++++++++++++++++-----------
 3 files changed, 166 insertions(+), 58 deletions(-)

diff --git a/crypto/Kconfig b/crypto/Kconfig
index dd14a2532c..528e9a0d22 100644
--- a/crypto/Kconfig
+++ b/crypto/Kconfig
@@ -135,33 +135,42 @@ config CRYPTO_PUBLIC_KEYS
 	depends on CRYPTO_BUILTIN_KEYS
 	string "public keys to compile in"
 	help
-	  This option should be a space separated list of filenames of
-	  PEM-formatted files containing X.509 certificates to be included into
-	  barebox. If an entry starts with "pkcs11:" it is interpreted as a
-	  PKCS#11 URI rather than a file. If an entry starts with a <hint>:
-	  prefix, <hint> is used as a key name hint to find a key without
-	  iterating over all keys.
+	  This option should be a space separated list of filenames of public
+	  key specifications. While other syntaxes are supported for legacy
+	  reasons, each entry should have the format
+	  keyring=<keyring>[,fit-hint=<hint>]:<crt>
+
+	  <keyring> specifies the keyring for the key to be included in (e.g.
+	  "fit" if this key is to be used for fitimage verification).
+
+	  <fit-hint> is used as a key name hint for fitimage verificaiton to
+	  find a key without iterating over all keys.
+
+	  <crt> specifies the path of a public key to be included in barebox.
+	  It can be a PEM-formatted file containing an X.509 certificate. If a
+	  certificate path starts with "pkcs11:" it is interpreted as a PKCS#11
+	  URI rather than a file.
 
 	  Placeholders such as __ENV__VAR_NAME can be used to look up the
-	  corresponding value in the environment variable VAR_NAME for both
-	  public key paths/URIs as well as key name hints.
+	  corresponding value in the environment variable VAR_NAME for public
+	  key paths/URIs as well as key name hints.
 
 	  Examples specified directly:
 
-	  - CONFIG_CRYPTO_PUBLIC_KEYS="pkcs11:object=foo"
-	  - CONFIG_CRYPTO_PUBLIC_KEYS="myhint:pkcs11:object=foo"
-	  - CONFIG_CRYPTO_PUBLIC_KEYS="myhint:pkcs11:object=foo /foobar/baz.der"
-	  - CONFIG_CRYPTO_PUBLIC_KEYS="myhint:pkcs11:object=foo myotherhint:/foobar/baz.der"
+	  - CONFIG_CRYPTO_PUBLIC_KEYS="keyring=fit:pkcs11:object=foo"
+	  - CONFIG_CRYPTO_PUBLIC_KEYS="keyring=fit,fit-hint=myhint:pkcs11:object=foo"
+	  - CONFIG_CRYPTO_PUBLIC_KEYS="keyring=fit,fit-hint=myhint:pkcs11:object=foo keyring=fit:/foobar/baz.der"
+	  - CONFIG_CRYPTO_PUBLIC_KEYS="keyring=fit,fit-hint=myhint:pkcs11:object=foo keyring=fit,fit-hint=myotherhint:/foobar/baz.der"
 
 	  Example specified indirectly by two environment variables:
 
 	  - myhint="myhint"
 	  - myname="pkcs11:object=foo" (.der could be used too)
-	  - CONFIG_CRYPTO_PUBLIC_KEYS="__ENV__myhint:__ENV__myname"
+	  - CONFIG_CRYPTO_PUBLIC_KEYS="keyring=fit,fit-hint=__ENV__myhint:__ENV__myname"
 
 	  Example specified indirectly by a single environment variable:
 
-	  - mykey="myhint:pkcs11:object=foo" (.der could be used too)
+	  - mykey="keyring=fit,fit-hint=myhint:pkcs11:object=foo" (.der could be used too)
 	  - CONFIG_CRYPTO_PUBLIC_KEYS="__ENV__mykey"
 
 endmenu
diff --git a/include/crypto/public_key.h b/include/crypto/public_key.h
index 5c0234acc0..612efa8f38 100644
--- a/include/crypto/public_key.h
+++ b/include/crypto/public_key.h
@@ -15,6 +15,7 @@ enum public_key_type {
 struct public_key {
 	enum public_key_type type;
 	const char *key_name_hint;
+	char *keyring;
 	const unsigned char *hash;
 	unsigned int hashlen;
 
diff --git a/scripts/keytoc.c b/scripts/keytoc.c
index 9d6ec376c1..78e56930bc 100644
--- a/scripts/keytoc.c
+++ b/scripts/keytoc.c
@@ -25,6 +25,15 @@
 #include <openssl/param_build.h>
 #include <openssl/store.h>
 
+#include <stdbool.h>
+#include <ctype.h>
+
+struct keyinfo {
+	char *keyname;
+	char *keyring;
+	char *path;
+};
+
 static int dts, standalone;
 
 static void enomem_exit(const char *func)
@@ -491,7 +500,7 @@ static int print_hash(EVP_PKEY *key)
 	return ret ? -EINVAL : 0;
 }
 
-static int gen_key_ecdsa(EVP_PKEY *key, const char *key_name, const char *key_name_c)
+static int gen_key_ecdsa(EVP_PKEY *key, const char *key_name, const char *keyring, const char *key_name_c)
 {
 	char group[128];
 	size_t outlen;
@@ -553,6 +562,7 @@ static int gen_key_ecdsa(EVP_PKEY *key, const char *key_name, const char *key_na
 			fprintf(outfilep, "\nstatic struct public_key %s_public_key = {\n", key_name_c);
 			fprintf(outfilep, "\t.type = PUBLIC_KEY_TYPE_ECDSA,\n");
 			fprintf(outfilep, "\t.key_name_hint = \"%s\",\n", key_name);
+			fprintf(outfilep, "\t.keyring = \"%s\",\n", keyring);
 			fprintf(outfilep, "\t.hash = %s_hash,\n", key_name_c);
 			fprintf(outfilep, "\t.hashlen = %u,\n", SHA256_DIGEST_LENGTH);
 			fprintf(outfilep, "\t.ecdsa = &%s,\n", key_name_c);
@@ -565,9 +575,9 @@ static int gen_key_ecdsa(EVP_PKEY *key, const char *key_name, const char *key_na
 	return 0;
 }
 
-static const char *try_resolve_env(const char *input)
+static char *try_resolve_env(char *input)
 {
-	const char *var;
+	char *var;
 
 	if (strncmp(input, "__ENV__", 7))
 		return input;
@@ -583,7 +593,7 @@ static const char *try_resolve_env(const char *input)
 	return var;
 }
 
-static int gen_key_rsa(EVP_PKEY *key, const char *key_name, const char *key_name_c)
+static int gen_key_rsa(EVP_PKEY *key, const char *key_name, const char *keyring, const char *key_name_c)
 {
 	BIGNUM *modulus, *r_squared;
 	uint64_t exponent = 0;
@@ -659,6 +669,7 @@ static int gen_key_rsa(EVP_PKEY *key, const char *key_name, const char *key_name
 			fprintf(outfilep, "\nstatic struct public_key %s_public_key = {\n", key_name_c);
 			fprintf(outfilep, "\t.type = PUBLIC_KEY_TYPE_RSA,\n");
 			fprintf(outfilep, "\t.key_name_hint = \"%s\",\n", key_name);
+			fprintf(outfilep, "\t.keyring = \"%s\",\n", keyring);
 			fprintf(outfilep, "\t.hash = %s_hash,\n", key_name_c);
 			fprintf(outfilep, "\t.hashlen = %u,\n", SHA256_DIGEST_LENGTH);
 			fprintf(outfilep, "\t.rsa = &%s,\n", key_name_c);
@@ -671,18 +682,18 @@ static int gen_key_rsa(EVP_PKEY *key, const char *key_name, const char *key_name
 	return 0;
 }
 
-static int gen_key(const char *keyname, const char *path)
+static int gen_key(struct keyinfo *info)
 {
 	int ret;
 	EVP_PKEY *key;
 	char *tmp, *key_name_c;
 
 	/* key name handling */
-	keyname = try_resolve_env(keyname);
-	if (!keyname)
+	info->keyname = try_resolve_env(info->keyname);
+	if (!info->keyname)
 		exit(1);
 
-	tmp = key_name_c = strdup(keyname);
+	tmp = key_name_c = strdup(info->keyname);
 
 	while (*tmp) {
 		if (*tmp == '-')
@@ -691,32 +702,104 @@ static int gen_key(const char *keyname, const char *path)
 	}
 
 	/* path/URI handling */
-	path = try_resolve_env(path);
-	if (!path)
+	info->path = try_resolve_env(info->path);
+	if (!info->path)
 		exit(1);
 
-	if (!strncmp(path, "pkcs11:", 7)) {
-		ret = engine_get_pub_key(path, &key);
+	if (!strncmp(info->path, "pkcs11:", 7)) {
+		ret = engine_get_pub_key(info->path, &key);
 		if (ret)
 			exit(1);
 	} else {
-		ret = pem_get_pub_key(path, &key);
+		ret = pem_get_pub_key(info->path, &key);
 		if (ret)
 			exit(1);
 	}
 
 	/* generate built-in keys */
-	ret = gen_key_ecdsa(key, keyname, key_name_c);
+	ret = gen_key_ecdsa(key, info->keyname, info->keyring, key_name_c);
 	if (ret == -EOPNOTSUPP)
 		return ret;
 
 	if (ret)
-		ret = gen_key_rsa(key, keyname, key_name_c);
+		ret = gen_key_rsa(key, info->keyname, info->keyring, key_name_c);
 
 	return ret;
 }
 
-static void get_name_path(const char *keyspec, char **keyname, char **path)
+static bool is_identifier(char **s)
+{
+	char *p = *s;
+
+	/* [a-zA-Z] */
+	if (!isalpha(*p))
+		return false;
+	p++;
+
+	/* [a-zA-Z0-9_-]* */
+	while (isalnum(*p) || *p == '_' || *p == '-')
+		p++;
+
+	*s = p;
+	return true;
+}
+
+static bool parse_info(char *p, struct keyinfo *out)
+{
+	char *k = p;
+	char *v;
+
+	if (*p == '\0') /* spec starts with colon. This is valid. */
+		return true;
+
+	if (!is_identifier(&p))
+		return false;
+
+	if (*p == '\0') {
+		out->keyname = strdup(k);
+		if (!k)
+			enomem_exit(__func__);
+		return true; /* legacy syntax */
+	}
+
+	/* new syntax: `<k>=<v>[,k=v[...]]` */
+	for (;;) {
+		if (*p != '=')
+			return false;
+		*p = '\0';
+
+		p++;
+		/* read `<k>=`, excepting <v> */
+		v = p;
+		if (!is_identifier(&p))
+			return false;
+
+		if (*p == '\0' || *p == ',') {
+			char d = *p;
+			*p = '\0';
+			v = strdup(v);
+			if (!v)
+				enomem_exit(__func__);
+			if (strcmp(k, "keyring") == 0)
+				out->keyring = strdup(v);
+			else if (strcmp(k, "hint") == 0)
+				out->keyname = strdup(v);
+			else
+				return false;
+
+			if (d == '\0')
+				return true;
+		} else {
+			return false;
+		}
+		p++;
+		k = p;
+		if (!is_identifier(&p))
+			return true;
+	}
+}
+
+static bool get_name_path(const char *keyspec, struct keyinfo *out)
 {
 	char *sep, *spec;
 
@@ -724,24 +807,24 @@ static void get_name_path(const char *keyspec, char **keyname, char **path)
 	if (!spec)
 		enomem_exit(__func__);
 
-	/* Split <key-hint>:<key-path> pair, <key-hint> is optional */
 	sep = strchr(spec, ':');
 	if (!sep) {
-		*path = spec;
-		return;
+		out->path = spec;
+		return true;
 	}
 
 	*sep = 0;
-	*keyname = strdup(spec);
-	if (!*keyname)
-		enomem_exit(__func__);
-
+	if (!parse_info(spec, out)) {
+		free(spec);
+		return false;
+	}
 	sep++;
-	*path = strdup(sep);
-	if (!*path)
+	out->path = strdup(sep);
+	if (!out->path)
 		enomem_exit(__func__);
 
 	free(spec);
+	return true;
 }
 
 int main(int argc, char *argv[])
@@ -749,6 +832,8 @@ int main(int argc, char *argv[])
 	int i, opt, ret;
 	char *outfile = NULL;
 	int keynum = 1;
+	int keycount;
+	struct keyinfo *keylist;
 
 	outfilep = stdout;
 
@@ -776,13 +861,33 @@ int main(int argc, char *argv[])
 	}
 
 	if (optind == argc) {
-		fprintf(stderr, "Usage: %s [-ods] <key_name_hint>:<crt> ...\n", argv[0]);
+		fprintf(stderr, "Usage: %s [-ods] keyring=<keyring>,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");
 		exit(1);
 	}
 
+	keycount = argc - optind;
+	keylist = calloc(sizeof(struct keyinfo), keycount);
+
+	for (i = 0; i < keycount; i++) {
+		const char *keyspec = argv[optind + i];
+		struct keyinfo *info = &keylist[i];
+
+		if (!keyspec)
+			exit(1);
+
+		if (!strncmp(keyspec, "pkcs11:", 7)) { // legacy format of pkcs11 URI
+			info->path = strdup(keyspec);
+		} else {
+			if (!get_name_path(keyspec, info)) {
+				fprintf(stderr, "invalid keyspec %i: %s\n", optind, keyspec);
+				exit(1);
+			}
+		}
+	}
+
 	if (dts) {
 		fprintf(outfilep, "/dts-v1/;\n");
 		fprintf(outfilep, "/ {\n");
@@ -795,32 +900,24 @@ int main(int argc, char *argv[])
 		fprintf(outfilep, "#include <crypto/rsa.h>\n");
 	}
 
-	for (i = optind; i < argc; i++) {
-		const char *keyspec = argv[i];
-		char *keyname = NULL;
-		char *path = NULL;
-
-		keyspec = try_resolve_env(keyspec);
-		if (!keyspec)
-			exit(1);
 
-		if (!strncmp(keyspec, "pkcs11:", 7))
-			path = strdup(keyspec);
-		else
-			get_name_path(keyspec, &keyname, &path);
+	for (i = 0; i < keycount; i++) {
+		struct keyinfo *info = &keylist[i];
 
-		if (!keyname) {
-			ret = asprintf(&keyname, "key_%d", keynum++);
+		if (!info->keyname) {
+			ret = asprintf(&info->keyname, "key_%d", keynum++);
 			if (ret < 0)
 				enomem_exit("asprintf");
 		}
 
-		ret = gen_key(keyname, path);
+		if (!info->keyring) {
+			info->keyring = strdup("fit");
+			fprintf(stderr, "Warning: No keyring provided in keyspec, defaulting to keyring=fit for %s\n", argv[optind + i]);
+		}
+
+		ret = gen_key(info);
 		if (ret)
 			exit(1);
-
-		free(keyname);
-		free(path);
 	}
 
 	if (dts) {
@@ -828,5 +925,6 @@ int main(int argc, char *argv[])
 		fprintf(outfilep, "};\n");
 	}
 
+	/* all keyinfo fields freed on exit */
 	exit(0);
 }

-- 
2.51.2.535.g419c72cb8a




  parent reply	other threads:[~2025-10-28 18:04 UTC|newest]

Thread overview: 18+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-10-28 18:03 [PATCH v2 00/17] TLV-Signature and keyrings Jonas Rebmann
2025-10-28 18:03 ` [PATCH v2 01/17] lib: idr: avoid dangling else in idr_for_each_entry() Jonas Rebmann
2025-10-28 18:03 ` [PATCH v2 02/17] common: clean up TLV code Jonas Rebmann
2025-10-28 18:03 ` Jonas Rebmann [this message]
2025-10-28 18:03 ` [PATCH v2 04/17] fit: only accept keys from "fit"-keyring Jonas Rebmann
2025-10-28 18:03 ` [PATCH v2 05/17] crypto: keytoc: Rename "hint" to "fit-hint" and do not use it in identifiers Jonas Rebmann
2025-10-28 18:03 ` [PATCH v2 06/17] commands: keys: update output format to include keyring Jonas Rebmann
2025-10-28 18:03 ` [PATCH v2 07/17] commands: tlv: Error out on invalid TLVs Jonas Rebmann
2025-10-28 18:03 ` [PATCH v2 08/17] scripts: bareboxtlv-generator: Implement signature Jonas Rebmann
2025-10-28 18:03 ` [PATCH v2 09/17] scripts: bareboxtlv-generator: Increase max_size in example schema Jonas Rebmann
2025-10-28 18:03 ` [PATCH v2 10/17] common: tlv: Add TLV-Signature support Jonas Rebmann
2025-10-28 18:03 ` [PATCH v2 11/17] common: tlv: default decoder for signed TLV Jonas Rebmann
2025-10-28 18:03 ` [PATCH v2 12/17] crypto: Use "development" keys for "fit" and "tlv" keyring Jonas Rebmann
2025-10-28 18:03 ` [PATCH v2 13/17] test: py: add signature to TLV integration tests Jonas Rebmann
2025-10-28 18:03 ` [PATCH v2 14/17] ci: pytest: Add kconfig fragment for TLV signature " Jonas Rebmann
2025-10-28 18:03 ` [PATCH v2 15/17] crypto: concatenate fit development certificate with private key Jonas Rebmann
2025-10-28 18:03 ` [PATCH v2 16/17] doc/barebox-tlv: Update documentation regarding TLV-Signature Jonas Rebmann
2025-10-28 18:03 ` [PATCH v2 17/17] Documentation: migration-2025.11.0: List changes to CONFIG_CRYPTO_PUBLIC_KEYS Jonas Rebmann

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20251028-tlv-signature-v2-3-3bafce636ad7@pengutronix.de \
    --to=jre@pengutronix.de \
    --cc=a.fatoum@pengutronix.de \
    --cc=barebox@lists.infradead.org \
    --cc=s.hauer@pengutronix.de \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox