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:
parent
c60d06057c
commit
ec3dea2b45
7 changed files with 276 additions and 124 deletions
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
130
subsys/jwt/jwt.c
130
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 <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
15
subsys/jwt/jwt.h
Normal 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_ */
|
||||
82
subsys/jwt/jwt_legacy_ecdsa.c
Normal file
82
subsys/jwt/jwt_legacy_ecdsa.c
Normal 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;
|
||||
}
|
||||
56
subsys/jwt/jwt_legacy_rsa.c
Normal file
56
subsys/jwt/jwt_legacy_rsa.c
Normal 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
51
subsys/jwt/jwt_psa.c
Normal 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;
|
||||
}
|
||||
Loading…
Reference in a new issue