Skip to content

Commit

Permalink
Work on coverage
Browse files Browse the repository at this point in the history
- Added ed448 key
  - Not supported on GnuTLS (shake256 not available)

- Add bad JWK keyring

Signed-off-by: Ben Collins <[email protected]>
  • Loading branch information
benmcollins committed Jan 8, 2025
1 parent ca046f2 commit 322546c
Show file tree
Hide file tree
Showing 13 changed files with 374 additions and 90 deletions.
6 changes: 3 additions & 3 deletions include/jwt.h
Original file line number Diff line number Diff line change
Expand Up @@ -934,7 +934,7 @@ jwk_set_t *jwks_load(jwk_set_t *jwk_set, const char *jwk_json_str);
* or a jwt_set_t with error set. NULL generally means ENOMEM.
*/
JWT_EXPORT
jwk_set_t *jwks_load_strb(jwk_set_t *jwk_set, const char *jwk_json_str,
jwk_set_t *jwks_load_strn(jwk_set_t *jwk_set, const char *jwk_json_str,
const size_t len);

/**
Expand Down Expand Up @@ -978,10 +978,10 @@ JWT_EXPORT
jwk_set_t *jwks_create(const char *jwk_json_str);

/**
* @brief Wrapper around jwks_load_strb() that explicitly creates a new keyring
* @brief Wrapper around jwks_load_strn() that explicitly creates a new keyring
*/
JWT_EXPORT
jwk_set_t *jwks_create_strb(const char *jwk_json_str, const size_t len);
jwk_set_t *jwks_create_strn(const char *jwk_json_str, const size_t len);

/**
* @brief Wrapper around jwks_load_fromfile() that explicitly creates a new
Expand Down
103 changes: 71 additions & 32 deletions libjwt/gnutls/sign-verify.c
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
/* Copyright (C) 2017 Nicolas Mora <[email protected]>
Copyright (C) 2024-2025 maClara, LLC <[email protected]>
This file is part of the JWT C Library
SPDX-License-Identifier: MPL-2.0
Expand Down Expand Up @@ -31,7 +32,7 @@ static int gnutls_sign_sha_hmac(jwt_t *jwt, char **out, unsigned int *len,
size_t key_len;

if (!ops_compat(jwt->jw_key, JWT_CRYPTO_OPS_OPENSSL))
return 1;
return 1; // LCOV_EXCL_LINE

key = jwt->jw_key->oct.key;
key_len = jwt->jw_key->oct.len;
Expand All @@ -47,7 +48,7 @@ static int gnutls_sign_sha_hmac(jwt_t *jwt, char **out, unsigned int *len,
alg = GNUTLS_DIG_SHA512;
break;
default:
return 1;
return 1; // LCOV_EXCL_LINE
}

*len = gnutls_hmac_get_len(alg);
Expand All @@ -56,9 +57,11 @@ static int gnutls_sign_sha_hmac(jwt_t *jwt, char **out, unsigned int *len,
return 1;

if (gnutls_hmac_fast(alg, key, key_len, str, str_len, *out)) {
// LCOV_EXCL_START
jwt_freemem(*out);
*out = NULL;
return 1;
// LCOV_EXCL_STOP
}

return 0;
Expand All @@ -72,11 +75,11 @@ static int gnutls_verify_sha_hmac(jwt_t *jwt, const char *head,
int ret;

if (gnutls_sign_sha_hmac(jwt, &sig_check, &len, head, head_len))
return 1;
return 1; // LCOV_EXCL_LINE

ret = jwt_base64uri_encode(&buf, sig_check, len);
if (ret <= 0 || buf == NULL)
return -ret;
return 1; // LCOV_EXCL_LINE

ret = jwt_strcmp(sig, buf);

Expand All @@ -100,9 +103,8 @@ static int gnutls_sign_sha_pem(jwt_t *jwt, char **out, unsigned int *len,
}

if (jwt->jw_key->pem == NULL)
return 1;
return 1; // LCOV_EXCL_LINE

gnutls_x509_privkey_t key;
gnutls_privkey_t privkey;
gnutls_datum_t key_dat = {
(unsigned char *)jwt->jw_key->pem,
Expand All @@ -116,6 +118,25 @@ static int gnutls_sign_sha_pem(jwt_t *jwt, char **out, unsigned int *len,
int ret = 0, pk_alg;
int alg, adj;

if (gnutls_privkey_init(&privkey)) {
// LCOV_EXCL_START
jwt_write_error(jwt, "Error initializing privkey");
ret = 1;
goto sign_clean_key;
// LCOV_EXCL_STOP
}

/* Try loading as a private key, and extracting the pubkey */
if (gnutls_privkey_import_x509_raw(privkey, &key_dat,
GNUTLS_X509_FMT_PEM,
NULL, 0)) {
// LCOV_EXCL_START
jwt_write_error(jwt, "Error importing privkey");
ret = 1;
goto sign_clean_privkey;
// LCOV_EXCL_STOP
}

/* Initialize for checking later. */
*out = NULL;

Expand Down Expand Up @@ -165,34 +186,31 @@ static int gnutls_sign_sha_pem(jwt_t *jwt, char **out, unsigned int *len,

/* EdDSA */
case JWT_ALG_EDDSA:
alg = GNUTLS_DIG_SHA512;
pk_alg = GNUTLS_PK_EDDSA_ED25519;
pk_alg = gnutls_privkey_get_pk_algorithm(privkey, NULL);
if (pk_alg == GNUTLS_PK_EDDSA_ED25519)
alg = GNUTLS_DIG_SHA512;
else if (pk_alg == GNUTLS_PK_EDDSA_ED448) {
/* Not implemented in GnuTLS yet, will fail XXX */
jwt_write_error(jwt,
"ED448 is not yet implemented in GnuTLS");
alg = GNUTLS_DIG_SHAKE_256;
} else {
jwt_write_error(jwt, "Unknown EdDSA key type");
ret = 1;
goto sign_clean_privkey;
}
break;

default:
return 1;
}

/* Initialize signature process data */
if (gnutls_x509_privkey_init(&key))
return 1;

if (gnutls_x509_privkey_import(key, &key_dat, GNUTLS_X509_FMT_PEM)) {
ret = 1;
goto sign_clean_key;
}

if (gnutls_privkey_init(&privkey)) {
ret = 1;
goto sign_clean_key;
}

if (gnutls_privkey_import_x509(privkey, key, 0)) {
// LCOV_EXCL_START
jwt_write_error(jwt, "Unknown alg during signing");
ret = 1;
goto sign_clean_privkey;
// LCOV_EXCL_STOP
}

if (pk_alg != gnutls_privkey_get_pk_algorithm(privkey, NULL)) {
jwt_write_error(jwt, "Alg mismatch with key during signing");
ret = 1;
goto sign_clean_privkey;
}
Expand All @@ -203,8 +221,10 @@ static int gnutls_sign_sha_pem(jwt_t *jwt, char **out, unsigned int *len,

/* Sign data */
if (gnutls_privkey_sign_data(privkey, alg, 0, &body_dat, &sig_dat)) {
// LCOV_EXCL_START
ret = 1;
goto sign_clean_privkey;
// LCOV_EXCL_STOP
}

if (pk_alg == GNUTLS_PK_EC) {
Expand Down Expand Up @@ -236,8 +256,10 @@ static int gnutls_sign_sha_pem(jwt_t *jwt, char **out, unsigned int *len,

*out = jwt_malloc(out_size);
if (*out == NULL) {
// LCOV_EXCL_START
ret = 1;
goto sign_clean_privkey;
// LCOV_EXCL_STOP
}
memset(*out, 0, out_size);

Expand All @@ -253,8 +275,10 @@ static int gnutls_sign_sha_pem(jwt_t *jwt, char **out, unsigned int *len,
/* All others that aren't EC */
*out = jwt_malloc(sig_dat.size);
if (*out == NULL) {
// LCOV_EXCL_START
ret = 1;
goto sign_clean_privkey;
// LCOV_EXCL_STOP
}

/* Copy signature to out */
Expand All @@ -269,11 +293,11 @@ static int gnutls_sign_sha_pem(jwt_t *jwt, char **out, unsigned int *len,
gnutls_privkey_deinit(privkey);

sign_clean_key:
gnutls_x509_privkey_deinit(key);

if (ret && *out) {
// LCOV_EXCL_START
jwt_freemem(*out);
*out = NULL;
// LCOV_EXCL_STOP
}

return ret;
Expand Down Expand Up @@ -301,8 +325,10 @@ static int gnutls_verify_sha_pem(jwt_t *jwt, const char *head,
};

if (jwt->alg == JWT_ALG_ES256K) {
// LCOV_EXCL_START
jwt_write_error(jwt, "ES256K Not Supported on GnuTLS");
return 1;
// LCOV_EXCL_STOP
}

switch (jwt->alg) {
Expand Down Expand Up @@ -346,7 +372,7 @@ static int gnutls_verify_sha_pem(jwt_t *jwt, const char *head,
break;

default:
return 1;
return 1; // LCOV_EXCL_LINE
}

sig = (unsigned char *)jwt_base64uri_decode(sig_b64, &sig_len);
Expand All @@ -355,33 +381,44 @@ static int gnutls_verify_sha_pem(jwt_t *jwt, const char *head,
return 1;

if (gnutls_pubkey_init(&pubkey)) {
// LCOV_EXCL_START
ret = 1;
goto verify_clean_sig;
// LCOV_EXCL_STOP
}

if ((ret = gnutls_pubkey_import(pubkey, &cert_dat, GNUTLS_X509_FMT_PEM))) {
ret = gnutls_pubkey_import(pubkey, &cert_dat, GNUTLS_X509_FMT_PEM);
if (ret) {
gnutls_privkey_t privkey;

/* Try loading as a private key, and extracting the pubkey. This is pefectly
* legit. A JWK can have a private key with key_ops of SIGN and VERIFY. */
if (gnutls_privkey_init(&privkey)) {
// LCOV_EXCL_START
ret = 1;
goto verify_clean_pubkey;
// LCOV_EXCL_STOP
}

/* Try loading as a private key, and extracting the pubkey */
if (gnutls_privkey_import_x509_raw(privkey, &cert_dat, GNUTLS_X509_FMT_PEM, NULL, 0)) {
if (gnutls_privkey_import_x509_raw(privkey, &cert_dat,
GNUTLS_X509_FMT_PEM,
NULL, 0)) {
// LCOV_EXCL_START
ret = 1;
gnutls_privkey_deinit(privkey);
goto verify_clean_pubkey;
// LCOV_EXCL_STOP
}

ret = gnutls_pubkey_import_privkey(pubkey, privkey, 0, 0);
gnutls_privkey_deinit(privkey);

if (ret) {
// LCOV_EXCL_START
ret = 1;
goto verify_clean_pubkey;
// LCOV_EXCL_STOP
}
}

Expand Down Expand Up @@ -409,13 +446,15 @@ static int gnutls_verify_sha_pem(jwt_t *jwt, const char *head,
s.size = 66;
s.data = sig + 66;
} else {
// LCOV_EXCL_START
ret = 1;
goto verify_clean_pubkey;
// LCOV_EXCL_STOP
}

if (gnutls_encode_rs_value(&sig_dat, &r, &s) ||
gnutls_pubkey_verify_data2(pubkey, alg, 0, &data, &sig_dat))
ret = 1;
ret = 1; // LCOV_EXCL_LINE

if (sig_dat.data != NULL)
gnutls_free(sig_dat.data);
Expand Down
15 changes: 10 additions & 5 deletions libjwt/jwks.c
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,13 @@ static void jwk_process_values(json_t *jwk, jwk_item_t *item)

/* Start with the ALG (4.4). */
j_alg = json_object_get(jwk, "alg");
if (j_alg && json_is_string(j_alg))
if (j_alg) {
if (!json_is_string(j_alg)) {
jwt_write_error(item, "Invalid alg type");
return;
}
item->alg = jwt_str_alg(json_string_value(j_alg));
}

/* Check for use (4.2). */
j_use = json_object_get(jwk, "use");
Expand Down Expand Up @@ -376,7 +381,7 @@ static jwk_set_t *jwks_process(jwk_set_t *jwk_set, json_t *j_all, json_error_t *
}

#define __FLAG_EMPTY (void *)0xfffff00d
jwk_set_t *jwks_load_strb(jwk_set_t *jwk_set, const char *jwk_json_str,
jwk_set_t *jwks_load_strn(jwk_set_t *jwk_set, const char *jwk_json_str,
const size_t len)
{
json_auto_t *j_all = NULL;
Expand Down Expand Up @@ -412,7 +417,7 @@ jwk_set_t *jwks_load(jwk_set_t *jwk_set, const char *jwk_json_str)
len = strlen(real_str);
}

return jwks_load_strb(jwk_set, real_str, len);
return jwks_load_strn(jwk_set, real_str, len);
}

jwk_set_t *jwks_load_fromfile(jwk_set_t *jwk_set, const char *file_name)
Expand Down Expand Up @@ -458,9 +463,9 @@ jwk_set_t *jwks_create(const char *jwk_json_str)
return jwks_load(NULL, jwk_json_str);
}

jwk_set_t *jwks_create_strb(const char *jwk_json_str, const size_t len)
jwk_set_t *jwks_create_strn(const char *jwk_json_str, const size_t len)
{
return jwks_load_strb(NULL, jwk_json_str, len);
return jwks_load_strn(NULL, jwk_json_str, len);
}

jwk_set_t *jwks_create_fromfile(const char *file_name)
Expand Down
8 changes: 4 additions & 4 deletions libjwt/jwt.c
Original file line number Diff line number Diff line change
Expand Up @@ -404,23 +404,23 @@ static int __check_key_bits(jwt_t *jwt)
case JWT_ALG_EDDSA:
case JWT_ALG_ES256K:
case JWT_ALG_ES256:
if (key_bits == 256)
if (key_bits == 256 || key_bits == 456)
return 0;
jwt_write_error(jwt, "Key needs to be 256 bits. Has %d bits",
jwt_write_error(jwt, "Key needs to be 256 or 456 bits: %d bits",
key_bits);
break;

case JWT_ALG_ES384:
if (key_bits == 384)
return 0;
jwt_write_error(jwt, "Key needs to be 384 bits. Has %d bits",
jwt_write_error(jwt, "Key needs to be 384 bits: %d bits",
key_bits);
break;

case JWT_ALG_ES512:
if (key_bits == 521)
return 0;
jwt_write_error(jwt, "Key needs to be 521 bits. Has %d bits",
jwt_write_error(jwt, "Key needs to be 521 bits: %d bits",
key_bits);
break;

Expand Down
Loading

0 comments on commit 322546c

Please sign in to comment.