jwt: reshape and add alternative for ECDSA using PSA

This commit:

- creates 2 new files, jwt_ecdsa.c and jwt_rsa.c, to hold the
  implementations of the corresponding ECDSA/RSA signature
  algorithms;
- RSA signature is stil done through Mbed TLS's PK module which
  can optionally make use of PSA (if enabled);
- ECDSA signature will instead use PSA, if possible, or TinyCrypt
  as fallback.

Signed-off-by: Valerio Setti <vsetti@baylibre.com>
This commit is contained in:
Valerio Setti 2024-09-10 16:42:50 +02:00 committed by Anas Nashif
parent c60d06057c
commit ec3dea2b45
7 changed files with 276 additions and 124 deletions

View file

@ -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)

View file

@ -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

View file

@ -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 <zephyr/data/jwt.h>
#include <zephyr/data/json.h>
#ifdef CONFIG_JWT_SIGN_RSA
#include <mbedtls/pk.h>
#include <mbedtls/rsa.h>
#include <mbedtls/sha256.h>
#include <zephyr/random/random.h>
#endif
#include "jwt.h"
#ifdef CONFIG_JWT_SIGN_ECDSA
#include <tinycrypt/ctr_prng.h>
#include <tinycrypt/sha256.h>
#include <tinycrypt/ecc_dsa.h>
#include <tinycrypt/constants.h>
#include <zephyr/random/random.h>
#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,

15
subsys/jwt/jwt.h Normal file
View file

@ -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 <zephyr/data/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);
#endif /* ZEPHYR_SUBSYS_JWT_JWT_H_ */

View file

@ -0,0 +1,82 @@
/*
* Copyright (C) 2024 BayLibre SAS
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <string.h>
#include <zephyr/types.h>
#include <errno.h>
#include <zephyr/data/jwt.h>
#include <zephyr/data/json.h>
#include <zephyr/random/random.h>
#include <tinycrypt/ctr_prng.h>
#include <tinycrypt/sha256.h>
#include <tinycrypt/ecc_dsa.h>
#include <tinycrypt/constants.h>
#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;
}

View file

@ -0,0 +1,56 @@
/*
* Copyright (C) 2024 BayLibre SAS
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <string.h>
#include <zephyr/types.h>
#include <errno.h>
#include <zephyr/data/jwt.h>
#include <zephyr/data/json.h>
#include <mbedtls/pk.h>
#include <mbedtls/rsa.h>
#include <mbedtls/sha256.h>
#include <zephyr/random/random.h>
#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;
}

51
subsys/jwt/jwt_psa.c Normal file
View file

@ -0,0 +1,51 @@
/*
* Copyright (C) 2024 BayLibre SAS
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <string.h>
#include <zephyr/types.h>
#include <errno.h>
#include <zephyr/data/jwt.h>
#include <zephyr/data/json.h>
#include <psa/crypto.h>
#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;
}