diff --git a/subsys/jwt/CMakeLists.txt b/subsys/jwt/CMakeLists.txt index ef59560a5e5..82c65f11f41 100644 --- a/subsys/jwt/CMakeLists.txt +++ b/subsys/jwt/CMakeLists.txt @@ -2,4 +2,9 @@ zephyr_library() zephyr_library_sources(jwt.c) + +zephyr_library_sources_ifdef(CONFIG_JWT_SIGN_ECDSA_LEGACY jwt_legacy_ecdsa.c) +zephyr_library_sources_ifdef(CONFIG_JWT_SIGN_RSA_LEGACY jwt_legacy_rsa.c) +zephyr_library_sources_ifdef(CONFIG_JWT_USE_PSA jwt_psa.c) + zephyr_library_link_libraries_ifdef(CONFIG_MBEDTLS mbedTLS) diff --git a/subsys/jwt/Kconfig b/subsys/jwt/Kconfig index af4ea8a9a38..651fe46cbf5 100644 --- a/subsys/jwt/Kconfig +++ b/subsys/jwt/Kconfig @@ -1,4 +1,5 @@ # Copyright (c) 2018 Linaro +# Copyright (c) 2024 BayLibre SAS # SPDX-License-Identifier: Apache-2.0 menuconfig JWT @@ -7,27 +8,73 @@ menuconfig JWT help Enable creation of JWT tokens +if JWT + choice prompt "JWT signature algorithm" default JWT_SIGN_RSA - depends on JWT help Select which algorithm to use for signing JWT tokens. config JWT_SIGN_RSA bool "Use RSA signature (RS-256)" - depends on CSPRNG_ENABLED - select MBEDTLS - select MBEDTLS_KEY_EXCHANGE_RSA_ENABLED - select PSA_WANT_KEY_TYPE_RSA_PUBLIC_KEY if PSA_CRYPTO_CLIENT - select PSA_WANT_KEY_TYPE_RSA_KEY_PAIR_IMPORT if PSA_CRYPTO_CLIENT config JWT_SIGN_ECDSA bool "Use ECDSA signature (ES-256)" + +endchoice + +choice + default JWT_USE_PSA + prompt "Select crypto library to be used" + +config JWT_USE_PSA + bool "PSA crypto API library" + select MBEDTLS if !BUILD_WITH_TFM + select MBEDTLS_PSA_CRYPTO_C if !BUILD_WITH_TFM + +config JWT_USE_LEGACY + bool "Legacy library: TinyCrypt for ECDSA, Mbed TLS for RSA" + +endchoice + +# Prompless Kconfigs to effectively select which algorithm and library will be used +# to sign the JWT. User's selections on the above choices will determine which +# element will be picked here. +config JWT_SIGN_ECDSA_PSA + bool + default y + depends on JWT_SIGN_ECDSA && JWT_USE_PSA + select PSA_WANT_KEY_TYPE_ECC_KEY_PAIR_IMPORT + select PSA_WANT_ALG_ECDSA + select PSA_WANT_ECC_SECP_R1_256 + select PSA_WANT_ALG_SHA_256 + +config JWT_SIGN_ECDSA_LEGACY + bool + default y + depends on JWT_SIGN_ECDSA && JWT_USE_LEGACY select TINYCRYPT select TINYCRYPT_SHA256 select TINYCRYPT_ECC_DSA select TINYCRYPT_CTR_PRNG select TINYCRYPT_AES -endchoice +config JWT_SIGN_RSA_PSA + bool + default y + depends on JWT_SIGN_RSA && JWT_USE_PSA + select PSA_WANT_KEY_TYPE_RSA_PUBLIC_KEY + select PSA_WANT_KEY_TYPE_RSA_KEY_PAIR_IMPORT + select PSA_WANT_ALG_RSA_PKCS1V15_SIGN + select PSA_WANT_ALG_SHA_256 + +config JWT_SIGN_RSA_LEGACY + bool + default y + depends on JWT_SIGN_RSA && JWT_USE_LEGACY + depends on CSPRNG_ENABLED + select MBEDTLS + select MBEDTLS_KEY_EXCHANGE_RSA_ENABLED + +endif # JWT diff --git a/subsys/jwt/jwt.c b/subsys/jwt/jwt.c index ede21858320..4487e557096 100644 --- a/subsys/jwt/jwt.c +++ b/subsys/jwt/jwt.c @@ -1,5 +1,6 @@ /* * Copyright (C) 2018 Linaro Ltd + * Copyright (C) 2024 BayLibre SAS * * SPDX-License-Identifier: Apache-2.0 */ @@ -11,20 +12,12 @@ #include #include -#ifdef CONFIG_JWT_SIGN_RSA -#include -#include -#include -#include -#endif +#include "jwt.h" -#ifdef CONFIG_JWT_SIGN_ECDSA -#include -#include -#include -#include - -#include +#if defined(CONFIG_JWT_SIGN_RSA) +#define JWT_SIGNATURE_LEN 256 +#else /* CONFIG_JWT_SIGN_ECDSA */ +#define JWT_SIGNATURE_LEN 64 #endif /* @@ -153,8 +146,7 @@ static int jwt_add_header(struct jwt_builder *builder) #ifdef CONFIG_JWT_SIGN_RSA /* {"alg":"RS256","typ":"JWT"} */ "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9"; -#endif -#ifdef CONFIG_JWT_SIGN_ECDSA +#else /* CONFIG_JWT_SIGN_ECDSA */ /* {"alg":"ES256","typ":"JWT"} */ "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9"; #endif @@ -190,120 +182,24 @@ int jwt_add_payload(struct jwt_builder *builder, return res; } -#ifdef CONFIG_JWT_SIGN_RSA - -static int csprng_wrapper(void *ctx, unsigned char *dest, size_t size) -{ - ARG_UNUSED(ctx); - - return sys_csrand_get((void *)dest, size); -} - int jwt_sign(struct jwt_builder *builder, const char *der_key, size_t der_key_len) { - int res; - mbedtls_pk_context ctx; + int ret; + unsigned char sig[JWT_SIGNATURE_LEN]; - mbedtls_pk_init(&ctx); - - res = mbedtls_pk_parse_key(&ctx, der_key, der_key_len, - NULL, 0, csprng_wrapper, NULL); - if (res != 0) { - return res; - } - - uint8_t hash[32], sig[256]; - size_t sig_len = sizeof(sig); - - /* - * The '0' indicates to mbedtls to do a SHA256, instead of - * 224. - */ - mbedtls_sha256(builder->base, builder->buf - builder->base, - hash, 0); - - res = mbedtls_pk_sign(&ctx, MBEDTLS_MD_SHA256, - hash, sizeof(hash), - sig, sig_len, &sig_len, - csprng_wrapper, NULL); - if (res != 0) { - return res; - } - - base64_outch(builder, '.'); - base64_append_bytes(sig, sig_len, builder); - base64_flush(builder); - - return builder->overflowed ? -ENOMEM : 0; -} -#endif - -#ifdef CONFIG_JWT_SIGN_ECDSA -static TCCtrPrng_t prng_state; -static bool prng_init; - -static const char personalize[] = "zephyr:drivers/jwt/jwt.c"; - -static int setup_prng(void) -{ - if (prng_init) { - return 0; - } - prng_init = true; - - uint8_t entropy[TC_AES_KEY_SIZE + TC_AES_BLOCK_SIZE]; - - sys_rand_get(entropy, sizeof(entropy)); - - int res = tc_ctr_prng_init(&prng_state, - (const uint8_t *) &entropy, sizeof(entropy), - personalize, - sizeof(personalize)); - - return res == TC_CRYPTO_SUCCESS ? 0 : -EINVAL; -} - -int default_CSPRNG(uint8_t *dest, unsigned int size) -{ - int res = tc_ctr_prng_generate(&prng_state, NULL, 0, dest, size); - return res; -} - -int jwt_sign(struct jwt_builder *builder, - const char *der_key, - size_t der_key_len) -{ - struct tc_sha256_state_struct ctx; - uint8_t hash[32], sig[64]; - int res; - - tc_sha256_init(&ctx); - tc_sha256_update(&ctx, builder->base, builder->buf - builder->base); - tc_sha256_final(hash, &ctx); - - res = setup_prng(); - - if (res != 0) { - return res; - } - uECC_set_rng(&default_CSPRNG); - - /* Note that tinycrypt only supports P-256. */ - res = uECC_sign(der_key, hash, sizeof(hash), - sig, &curve_secp256r1); - if (res != TC_CRYPTO_SUCCESS) { - return -EINVAL; + ret = jwt_sign_impl(builder, der_key, der_key_len, sig, sizeof(sig)); + if (ret < 0) { + return ret; } base64_outch(builder, '.'); base64_append_bytes(sig, sizeof(sig), builder); base64_flush(builder); - return 0; + return builder->overflowed ? -ENOMEM : 0; } -#endif int jwt_init_builder(struct jwt_builder *builder, char *buffer, diff --git a/subsys/jwt/jwt.h b/subsys/jwt/jwt.h new file mode 100644 index 00000000000..3394461c9fa --- /dev/null +++ b/subsys/jwt/jwt.h @@ -0,0 +1,15 @@ +/* + * Copyright (C) 2024 BayLibre SAS + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_SUBSYS_JWT_JWT_H_ +#define ZEPHYR_SUBSYS_JWT_JWT_H_ + +#include + +int jwt_sign_impl(struct jwt_builder *builder, const unsigned char *der_key, + size_t der_key_len, unsigned char *sig, size_t sig_size); + +#endif /* ZEPHYR_SUBSYS_JWT_JWT_H_ */ diff --git a/subsys/jwt/jwt_legacy_ecdsa.c b/subsys/jwt/jwt_legacy_ecdsa.c new file mode 100644 index 00000000000..d8368280270 --- /dev/null +++ b/subsys/jwt/jwt_legacy_ecdsa.c @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2024 BayLibre SAS + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include "jwt.h" + +static TCCtrPrng_t prng_state; +static bool prng_init; + +static const char personalize[] = "zephyr:drivers/jwt/jwt.c"; + +static int setup_prng(void) +{ + if (prng_init) { + return 0; + } + prng_init = true; + + uint8_t entropy[TC_AES_KEY_SIZE + TC_AES_BLOCK_SIZE]; + + sys_rand_get(entropy, sizeof(entropy)); + + int res = tc_ctr_prng_init(&prng_state, (const uint8_t *)&entropy, sizeof(entropy), + personalize, sizeof(personalize)); + + return res == TC_CRYPTO_SUCCESS ? 0 : -EINVAL; +} + +/* This function is declared in + * modules/crypto/tinycrypt/lib/include/tinycrypt/ecc_platform_specific.h. + * + * TinyCrypt expects this function to be implemented somewhere when using the + * ECC module. + */ +int default_CSPRNG(uint8_t *dest, unsigned int size) +{ + int res = tc_ctr_prng_generate(&prng_state, NULL, 0, dest, size); + return res; +} + +int jwt_sign_impl(struct jwt_builder *builder, const unsigned char *der_key, size_t der_key_len, + unsigned char *sig, size_t sig_size) +{ + struct tc_sha256_state_struct ctx; + uint8_t hash[32]; + int res; + + ARG_UNUSED(sig_size); + + tc_sha256_init(&ctx); + tc_sha256_update(&ctx, builder->base, builder->buf - builder->base); + tc_sha256_final(hash, &ctx); + + res = setup_prng(); + + if (res != 0) { + return res; + } + + /* Note that tinycrypt only supports P-256. */ + res = uECC_sign(der_key, hash, sizeof(hash), sig, &curve_secp256r1); + if (res != TC_CRYPTO_SUCCESS) { + return -EINVAL; + } + + return 0; +} diff --git a/subsys/jwt/jwt_legacy_rsa.c b/subsys/jwt/jwt_legacy_rsa.c new file mode 100644 index 00000000000..2eb0adc0ede --- /dev/null +++ b/subsys/jwt/jwt_legacy_rsa.c @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2024 BayLibre SAS + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include "jwt.h" + +static int csprng_wrapper(void *ctx, unsigned char *dest, size_t size) +{ + ARG_UNUSED(ctx); + + return sys_csrand_get((void *)dest, size); +} + +int jwt_sign_impl(struct jwt_builder *builder, const unsigned char *der_key, size_t der_key_len, + unsigned char *sig, size_t sig_size) +{ + int res; + mbedtls_pk_context ctx; + size_t sig_len_out; + + mbedtls_pk_init(&ctx); + + res = mbedtls_pk_parse_key(&ctx, der_key, der_key_len, NULL, 0, csprng_wrapper, NULL); + if (res != 0) { + return res; + } + + uint8_t hash[32]; + + /* + * The '0' indicates to mbedtls to do a SHA256, instead of + * 224. + */ + res = mbedtls_sha256(builder->base, builder->buf - builder->base, hash, 0); + if (res != 0) { + return res; + } + + res = mbedtls_pk_sign(&ctx, MBEDTLS_MD_SHA256, hash, sizeof(hash), sig, sig_size, + &sig_len_out, csprng_wrapper, NULL); + return res; +} diff --git a/subsys/jwt/jwt_psa.c b/subsys/jwt/jwt_psa.c new file mode 100644 index 00000000000..edbafa6fefe --- /dev/null +++ b/subsys/jwt/jwt_psa.c @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2024 BayLibre SAS + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include + +#include +#include +#include + +#include "jwt.h" + +int jwt_sign_impl(struct jwt_builder *builder, const unsigned char *der_key, size_t der_key_len, + unsigned char *sig, size_t sig_size) +{ + psa_status_t status; + psa_key_attributes_t attr = PSA_KEY_ATTRIBUTES_INIT; + psa_key_id_t key_id; + size_t sig_len_out; + psa_algorithm_t alg; + int ret; + +#if defined(CONFIG_JWT_SIGN_ECDSA) + psa_set_key_type(&attr, PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_SECP_R1)); + psa_set_key_algorithm(&attr, PSA_ALG_ECDSA(PSA_ALG_SHA_256)); + alg = PSA_ALG_ECDSA(PSA_ALG_SHA_256); +#else /* CONFIG_JWT_SIGN_RSA */ + psa_set_key_type(&attr, PSA_KEY_TYPE_RSA_KEY_PAIR); + psa_set_key_algorithm(&attr, PSA_ALG_RSA_PKCS1V15_SIGN(PSA_ALG_SHA_256)); + alg = PSA_ALG_RSA_PKCS1V15_SIGN(PSA_ALG_SHA_256); +#endif /* CONFIG_JWT_SIGN_ECDSA || CONFIG_JWT_SIGN_RSA */ + psa_set_key_usage_flags(&attr, PSA_KEY_USAGE_SIGN_MESSAGE); + + status = psa_import_key(&attr, der_key, der_key_len, &key_id); + if (status != PSA_SUCCESS) { + return -EINVAL; + } + + status = psa_sign_message(key_id, alg, + builder->base, builder->buf - builder->base, + sig, sig_size, &sig_len_out); + ret = (status == PSA_SUCCESS) ? 0 : -EINVAL; + + psa_destroy_key(key_id); + + return ret; +}