[PATCH 3/3] test: self: add JSON Web Token tests

Ahmad Fatoum a.fatoum at pengutronix.de
Mon Oct 23 07:31:23 PDT 2023


This simple test decodes a JSON Web token after verifying it and asserts
that the claim contained inside are as expected.

The RSA public key is the one used by https://jwt.io/ by default and
thus allowing easy experimentation. For future extensibility of the
tests, the private key is appended, but is not currently used.

As rsatoc has a build-time openssl dependency, which we would complicate
running the test suite everywhere, we ship a precompiled C file.

To regenerate, REGENERATE_RSATOC can be specified as build argument.
The reason, we don't use the standard make mechanism of file timestamps
is that after a git checkout, we aren't guaranteed that the shipped file
will be newer than the pem file, which renders the mechanism useless for
allowing users to build all unit tests without OpenSSL.

Signed-off-by: Ahmad Fatoum <a.fatoum at pengutronix.de>
---
 test/self/Kconfig                |   7 ++
 test/self/Makefile               |  11 ++-
 test/self/jwt.c                  | 157 +++++++++++++++++++++++++++++++
 test/self/jwt_test.pem           |  37 ++++++++
 test/self/jwt_test.pem.c_shipped |  49 ++++++++++
 5 files changed, 260 insertions(+), 1 deletion(-)
 create mode 100644 test/self/jwt.c
 create mode 100644 test/self/jwt_test.pem
 create mode 100644 test/self/jwt_test.pem.c_shipped

diff --git a/test/self/Kconfig b/test/self/Kconfig
index e7da07491a91..5850dc95973b 100644
--- a/test/self/Kconfig
+++ b/test/self/Kconfig
@@ -36,12 +36,15 @@ config SELFTEST_ENABLE_ALL
 	select SELFTEST_FS_RAMFS if FS_RAMFS
 	select SELFTEST_TFTP if FS_TFTP
 	select SELFTEST_JSON if JSMN
+	select SELFTEST_JWT if JWT
 	select SELFTEST_DIGEST if DIGEST
 	select SELFTEST_MMU if MMU
 	select SELFTEST_STRING
 	select SELFTEST_SETJMP if ARCH_HAS_SJLJ
 	select SELFTEST_REGULATOR if REGULATOR && OFDEVICE
 	select SELFTEST_TEST_COMMAND if CMD_TEST
+	help
+	  Selects all self-tests compatible with current configuration
 
 config SELFTEST_MALLOC
 	bool "malloc() selftest"
@@ -73,6 +76,10 @@ config SELFTEST_JSON
 	bool "JSON selftest"
 	depends on JSMN
 
+config SELFTEST_JWT
+	bool "JSON Web Token selftest"
+	depends on JWT
+
 config SELFTEST_MMU
 	bool "MMU remapping selftest"
 	select MEMTEST
diff --git a/test/self/Makefile b/test/self/Makefile
index 8168abf26278..24e78a186513 100644
--- a/test/self/Makefile
+++ b/test/self/Makefile
@@ -9,6 +9,7 @@ obj-$(CONFIG_SELFTEST_OF_MANIPULATION) += of_manipulation.o of_manipulation.dtb.
 obj-$(CONFIG_SELFTEST_ENVIRONMENT_VARIABLES) += envvar.o
 obj-$(CONFIG_SELFTEST_FS_RAMFS) += ramfs.o
 obj-$(CONFIG_SELFTEST_JSON) += json.o
+obj-$(CONFIG_SELFTEST_JWT) += jwt.o jwt_test.pem.o
 obj-$(CONFIG_SELFTEST_DIGEST) += digest.o
 obj-$(CONFIG_SELFTEST_MMU) += mmu.o
 obj-$(CONFIG_SELFTEST_STRING) += string.o
@@ -16,5 +17,13 @@ obj-$(CONFIG_SELFTEST_SETJMP) += setjmp.o
 obj-$(CONFIG_SELFTEST_REGULATOR) += regulator.o test_regulator.dtbo.o
 obj-$(CONFIG_SELFTEST_TEST_COMMAND) += test_command.o
 
-clean-files := *.dtb *.dtb.S .*.dtc .*.pre .*.dts *.dtb.z
+ifdef REGENERATE_RSATOC
+
+$(obj)/jwt_test.pem.c_shipped: $(src)/jwt_test.pem FORCE
+	$(call if_changed,rsa_keys,$(basename $(target-stem)):$<,-s)
+
+endif
+
+clean-files := *.dtb *.dtb.S .*.dtc .*.pre .*.dts *.dtb.z *
 clean-files += *.dtbo *.dtbo.S .*.dtso
+clean-files += *.pem.c
diff --git a/test/self/jwt.c b/test/self/jwt.c
new file mode 100644
index 000000000000..f37b44be22b8
--- /dev/null
+++ b/test/self/jwt.c
@@ -0,0 +1,157 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <common.h>
+#include <rsa.h>
+#include <bselftest.h>
+#include <crypto/jwt.h>
+#include <console.h>
+
+BSELFTEST_GLOBALS();
+
+static const jsmntok_t *check_token(const jsmntok_t *token,
+				    const char *claim,
+				    const char *payload,
+				    jsmntype_t expected_type,
+				    const char *expected_value)
+{
+	total_tests++;
+
+	if (token->type != expected_type) {
+		failed_tests++;
+		printf("claim %s has type mismatch: got %d, but %d expected\n",
+		       claim, token->type, expected_type);
+		return NULL;
+	}
+
+	total_tests++;
+
+	if (!jsmn_eq(expected_value, payload, token)) {
+		failed_tests++;
+		printf("claim %s: value has mismatch: got %.*s, but %s expected\n",
+		       claim, (int)(token->end - token->start),
+		       &payload[token->start], expected_value);
+		return NULL;
+	}
+
+	return token;
+}
+
+static const jsmntok_t *jwt_check_claim(const struct jwt *jwt,
+					const char *claim,
+					jsmntype_t expected_type,
+					const char *expected_value)
+{
+	const jsmntok_t *token;
+
+	total_tests++;
+
+	token = jwt_get_claim(jwt, claim);
+	if (!token) {
+		failed_tests++;
+		printf("claim %s couldn't be located\n", claim);
+		return NULL;
+	}
+
+	return check_token(token, claim, jwt_get_payload(jwt),
+			   expected_type, expected_value);
+}
+
+static const char jwt_rs256[] =
+	"  eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9."
+	"eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0."
+	"NHVaYe26MbtOYhSKkoKYdFVomg4i8ZJd8_-RU8VNbftc4TSMb4bXP3l3YlNWACwyXPGf"
+	"fz5aXHc6lty1Y2t4SWRqGteragsVdZufDn5BlnJl9pdR_kdVFUsra2rWKEofkZeIC4yW"
+	"ytE58sMIihvo9H1ScmmVwBcQP6XETqYd0aSHp1gOa9RdUPDvoXQ5oqygTqVtxaDr6wUF"
+	"KrKItgBMzWIdNZ6y7O9E0DhEPTbE9rfBo6KTFsHAZnMg4k68CDp2woYIaXbmYTWcvbzI"
+	"uHO7_37GT79XdIwkm95QJ7hYC9RiwrV7mesbY4PAahERJawntho0my942XheVLmGwLMBkQ\n \n";
+
+static void test_jwt(void)
+{
+	char *jwt_rs256_mangled, *ch;
+	struct jwt_key jwt_key;
+	struct jwt *jwt;
+	extern const struct rsa_public_key __key_jwt_test;
+	int old_loglevel;
+
+	jwt_key.alg = JWT_ALG_RS256;
+	jwt_key.material.rsa_pub = &__key_jwt_test;
+	total_tests++;
+
+	jwt = jwt_decode(jwt_rs256, &jwt_key);
+	if (IS_ERR(jwt)) {
+		printf("failed to parse jwt\n");
+		failed_tests++;
+	} else {
+		jwt_check_claim(jwt, "sub", JSMN_STRING, "1234567890");
+		jwt_check_claim(jwt, "name", JSMN_STRING, "John Doe");
+		jwt_check_claim(jwt, "admin", JSMN_PRIMITIVE, "true");
+		jwt_check_claim(jwt, "iat", JSMN_PRIMITIVE, "1516239022");
+
+		jwt_free(jwt);
+	}
+
+	/*
+	 * Following tests intentionally fail and JWT failures are intentionally
+	 * noisy, so we decrease logging a bit during their run
+	 */
+
+	old_loglevel = barebox_loglevel;
+	barebox_loglevel = MSG_CRIT;
+
+	jwt_rs256_mangled = strdup(jwt_rs256);
+	ch = &jwt_rs256_mangled[strlen(jwt_rs256_mangled) - 1];
+	*ch = *ch == '_' ? '-' : '_';
+
+	total_tests++;
+
+	jwt = jwt_decode(jwt_rs256_mangled, &jwt_key);
+	if (!IS_ERR(jwt)) {
+		printf("%s:%d expected JWT verification to fail\n", __func__, __LINE__);
+		failed_tests++;
+		jwt_free(jwt);
+	}
+
+	free(jwt_rs256_mangled);
+
+	jwt_rs256_mangled = strdup(jwt_rs256);
+	ch = &jwt_rs256_mangled[0];
+	*ch = *ch == '_' ? '-' : '_';
+
+	total_tests++;
+
+	jwt = jwt_decode(jwt_rs256_mangled, &jwt_key);
+	if (!IS_ERR(jwt)) {
+		printf("%s:%d expected JWT verification to fail\n", __func__, __LINE__);
+		failed_tests++;
+		jwt_free(jwt);
+	}
+
+	free(jwt_rs256_mangled);
+
+	total_tests++;
+
+	jwt_key.alg = JWT_ALG_RS384;
+
+	jwt = jwt_decode(jwt_rs256, &jwt_key);
+	if (!IS_ERR(jwt)) {
+		printf("%s:%d expected JWT verification to fail\n", __func__, __LINE__);
+		failed_tests++;
+		jwt_free(jwt);
+	}
+
+	total_tests++;
+
+	jwt_key.alg = JWT_ALG_NONE;
+
+	jwt = jwt_decode(jwt_rs256, &jwt_key);
+	if (!IS_ERR(jwt)) {
+		printf("%s:%d expected JWT verification to fail\n", __func__, __LINE__);
+		failed_tests++;
+		jwt_free(jwt);
+	}
+
+	barebox_loglevel = old_loglevel;
+}
+bselftest(parser, test_jwt);
diff --git a/test/self/jwt_test.pem b/test/self/jwt_test.pem
new file mode 100644
index 000000000000..349a5b6a47f0
--- /dev/null
+++ b/test/self/jwt_test.pem
@@ -0,0 +1,37 @@
+-----BEGIN PUBLIC KEY-----
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAu1SU1LfVLPHCozMxH2Mo
+4lgOEePzNm0tRgeLezV6ffAt0gunVTLw7onLRnrq0/IzW7yWR7QkrmBL7jTKEn5u
++qKhbwKfBstIs+bMY2Zkp18gnTxKLxoS2tFczGkPLPgizskuemMghRniWaoLcyeh
+kd3qqGElvW/VDL5AaWTg0nLVkjRo9z+40RQzuVaE8AkAFmxZzow3x+VJYKdjykkJ
+0iT9wCS0DRTXu269V264Vf/3jvredZiKRkgwlL9xNAwxXFg0x/XFw005UWVRIkdg
+cKWTjpBP2dPwVZ4WWC+9aGVd+Gyn1o0CLelf4rEjGoXbAAEgAqeGUxrcIlbjXfbc
+mwIDAQAB
+-----END PUBLIC KEY-----
+-----BEGIN PRIVATE KEY-----
+MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQC7VJTUt9Us8cKj
+MzEfYyjiWA4R4/M2bS1GB4t7NXp98C3SC6dVMvDuictGeurT8jNbvJZHtCSuYEvu
+NMoSfm76oqFvAp8Gy0iz5sxjZmSnXyCdPEovGhLa0VzMaQ8s+CLOyS56YyCFGeJZ
+qgtzJ6GR3eqoYSW9b9UMvkBpZODSctWSNGj3P7jRFDO5VoTwCQAWbFnOjDfH5Ulg
+p2PKSQnSJP3AJLQNFNe7br1XbrhV//eO+t51mIpGSDCUv3E0DDFcWDTH9cXDTTlR
+ZVEiR2BwpZOOkE/Z0/BVnhZYL71oZV34bKfWjQIt6V/isSMahdsAASACp4ZTGtwi
+VuNd9tybAgMBAAECggEBAKTmjaS6tkK8BlPXClTQ2vpz/N6uxDeS35mXpqasqskV
+laAidgg/sWqpjXDbXr93otIMLlWsM+X0CqMDgSXKejLS2jx4GDjI1ZTXg++0AMJ8
+sJ74pWzVDOfmCEQ/7wXs3+cbnXhKriO8Z036q92Qc1+N87SI38nkGa0ABH9CN83H
+mQqt4fB7UdHzuIRe/me2PGhIq5ZBzj6h3BpoPGzEP+x3l9YmK8t/1cN0pqI+dQwY
+dgfGjackLu/2qH80MCF7IyQaseZUOJyKrCLtSD/Iixv/hzDEUPfOCjFDgTpzf3cw
+ta8+oE4wHCo1iI1/4TlPkwmXx4qSXtmw4aQPz7IDQvECgYEA8KNThCO2gsC2I9PQ
+DM/8Cw0O983WCDY+oi+7JPiNAJwv5DYBqEZB1QYdj06YD16XlC/HAZMsMku1na2T
+N0driwenQQWzoev3g2S7gRDoS/FCJSI3jJ+kjgtaA7Qmzlgk1TxODN+G1H91HW7t
+0l7VnL27IWyYo2qRRK3jzxqUiPUCgYEAx0oQs2reBQGMVZnApD1jeq7n4MvNLcPv
+t8b/eU9iUv6Y4Mj0Suo/AU8lYZXm8ubbqAlwz2VSVunD2tOplHyMUrtCtObAfVDU
+AhCndKaA9gApgfb3xw1IKbuQ1u4IF1FJl3VtumfQn//LiH1B3rXhcdyo3/vIttEk
+48RakUKClU8CgYEAzV7W3COOlDDcQd935DdtKBFRAPRPAlspQUnzMi5eSHMD/ISL
+DY5IiQHbIH83D4bvXq0X7qQoSBSNP7Dvv3HYuqMhf0DaegrlBuJllFVVq9qPVRnK
+xt1Il2HgxOBvbhOT+9in1BzA+YJ99UzC85O0Qz06A+CmtHEy4aZ2kj5hHjECgYEA
+mNS4+A8Fkss8Js1RieK2LniBxMgmYml3pfVLKGnzmng7H2+cwPLhPIzIuwytXywh
+2bzbsYEfYx3EoEVgMEpPhoarQnYPukrJO4gwE2o5Te6T5mJSZGlQJQj9q4ZB2Dfz
+et6INsK0oG8XVGXSpQvQh3RUYekCZQkBBFcpqWpbIEsCgYAnM3DQf3FJoSnXaMhr
+VBIovic5l0xFkEHskAjFTevO86Fsz1C2aSeRKSqGFoOQ0tmJzBEs1R6KqnHInicD
+TQrKhArgLXX4v3CddjfTRJkFWDbE/CkvKZNOrcf1nhaGCPspRJj2KUkj1Fhl9Cnc
+dn/RsYEONbwQSjIfMPkvxF+8HQ==
+-----END PRIVATE KEY-----
diff --git a/test/self/jwt_test.pem.c_shipped b/test/self/jwt_test.pem.c_shipped
new file mode 100644
index 000000000000..2142ae15dfb6
--- /dev/null
+++ b/test/self/jwt_test.pem.c_shipped
@@ -0,0 +1,49 @@
+#include <rsa.h>
+
+static uint32_t jwt_test_modulus[] = {
+	0x5df6dc9b, 0xdc2256e3, 0xa786531a, 0x00012002,
+	0x231a85db, 0xe95fe2b1, 0xd68d022d, 0x5df86ca7,
+	0x2fbd6865, 0x559e1658, 0x4fd9d3f0, 0xa5938e90,
+	0x22476070, 0x39516551, 0xf5c5c34d, 0x5c5834c7,
+	0x71340c31, 0x483094bf, 0x75988a46, 0xf78efade,
+	0x6eb855ff, 0xbb6ebd57, 0xb40d14d7, 0x24fdc024,
+	0xca4909d2, 0x4960a763, 0x8c37c7e5, 0x166c59ce,
+	0x84f00900, 0x1433b956, 0xf73fb8d1, 0xd5923468,
+	0x64e0d272, 0x0cbe4069, 0x25bd6fd5, 0xddeaa861,
+	0x7327a191, 0xe259aa0b, 0x63208519, 0xcec92e7a,
+	0x0f2cf822, 0xd15ccc69, 0x2f1a12da, 0x209d3c4a,
+	0x6664a75f, 0xb3e6cc63, 0x9f06cb48, 0xa2a16f02,
+	0x127e6efa, 0x4bee34ca, 0xb424ae60, 0x5bbc9647,
+	0xead3f233, 0x89cb467a, 0x5532f0ee, 0x2dd20ba7,
+	0x357a7df0, 0x46078b7b, 0xf3366d2d, 0x580e11e3,
+	0x1f6328e2, 0xc2a33331, 0xb7d52cf1, 0xbb5494d4,
+};
+
+static uint32_t jwt_test_rr[] = {
+	0xec4954b7, 0x61f69199, 0x9e489481, 0x14f25ec8,
+	0x712de1ab, 0x9c4ed93b, 0xcff16ec3, 0xb6e0c808,
+	0x56551022, 0x1206f0dc, 0x72051e96, 0x6ab07919,
+	0x8d29bea3, 0xa2a79109, 0x18a5e53d, 0x0a1ed2ae,
+	0xae6544f4, 0x5fb16424, 0x5253250c, 0x3fc04654,
+	0x9b9a3028, 0xf7219ed8, 0x8f9a7d60, 0x1020027e,
+	0xa7bb0182, 0xca68b839, 0x86a507ca, 0x725d9efb,
+	0xf43e09cd, 0xd373027e, 0x6c22f55c, 0x074bee70,
+	0x49525052, 0x0506900e, 0xf51bde0d, 0xc8f82c0e,
+	0x4a00d71e, 0x0a517ae2, 0x616e76fb, 0xb17b75d0,
+	0x4bfcbb90, 0x3efd07cf, 0xaf30c7cb, 0xa18dee7f,
+	0x02ed9615, 0x9185d985, 0x630a209e, 0xef23435c,
+	0x46277653, 0x57d47ec5, 0x86e58fcf, 0x8f0ebe09,
+	0x3b26c77e, 0xa3ef370d, 0xf83df63e, 0xa30a742e,
+	0x49c2fb64, 0xea9fbed9, 0xb7471da7, 0x7a411345,
+	0x303732ed, 0x6660f318, 0xe3a7df4c, 0x6a784bd5,
+};
+
+struct rsa_public_key __key_jwt_test;
+struct rsa_public_key __key_jwt_test = {
+	.len = 64,
+	.n0inv = 0x17d8566d,
+	.modulus = jwt_test_modulus,
+	.rr = jwt_test_rr,
+	.exponent = 0x10001,
+	.key_name_hint = "jwt_test",
+};
-- 
2.39.2




More information about the barebox mailing list