diff --git a/src/base64.c b/src/base64.c index 9ffe14d..79dc78f 100644 --- a/src/base64.c +++ b/src/base64.c @@ -73,6 +73,12 @@ static inline bool _decode(const char *input, size_t inlen, uint8_t **output, si // rlen takes a best guess on size; // might be too large for base64url, but never too small. + if (inlen > SIZE_MAX / 3) + { + CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG); + return false; + } + size_t rlen = ((inlen * 3) >> 2) + 3; uint8_t *buffer = cjose_get_alloc()(sizeof(uint8_t) * rlen); if (NULL == buffer) diff --git a/src/concatkdf.c b/src/concatkdf.c index 44ced67..43f456e 100644 --- a/src/concatkdf.c +++ b/src/concatkdf.c @@ -6,6 +6,7 @@ */ #include "include/concatkdf_int.h" +#include "include/util_int.h" #ifdef _WIN32 #include @@ -149,7 +150,7 @@ uint8_t *cjose_concatkdf_derive(const size_t keylen, concatkdf_derive_finish: EVP_MD_CTX_destroy(ctx); - cjose_get_dealloc()(buffer); + _cjose_cleanse_dealloc(buffer, keylen); return derived; } diff --git a/src/header.c b/src/header.c index 5a74797..728c4e1 100644 --- a/src/header.c +++ b/src/header.c @@ -6,6 +6,7 @@ */ #include +#include #include #include "cjose/header.h" #include "include/header_int.h" @@ -49,6 +50,59 @@ const char *CJOSE_HDR_EPK = "epk"; const char *CJOSE_HDR_APU = "apu"; const char *CJOSE_HDR_APV = "apv"; +static const char *CJOSE_HDR_CRIT = "crit"; + +//////////////////////////////////////////////////////////////////////////////// +bool _cjose_header_validate_crit(cjose_header_t *header, const char *const *supported, size_t supported_len, cjose_err *err) +{ + if (NULL == header) + { + return true; + } + + json_t *crit = json_object_get((json_t *)header, CJOSE_HDR_CRIT); + if (NULL == crit) + { + return true; + } + + if (!json_is_array(crit)) + { + CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG); + return false; + } + + size_t index = 0; + json_t *entry = NULL; + json_array_foreach(crit, index, entry) + { + if (!json_is_string(entry)) + { + CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG); + return false; + } + + const char *name = json_string_value(entry); + bool found = false; + for (size_t i = 0; i < supported_len; i++) + { + if (0 == strcmp(name, supported[i])) + { + found = true; + break; + } + } + + if (!found) + { + CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG); + return false; + } + } + + return true; +} + //////////////////////////////////////////////////////////////////////////////// cjose_header_t *cjose_header_new(cjose_err *err) { diff --git a/src/include/header_int.h b/src/include/header_int.h index 012a738..9695d15 100644 --- a/src/include/header_int.h +++ b/src/include/header_int.h @@ -8,6 +8,10 @@ #ifndef SRC_HEADER_INT_H #define SRC_HEADER_INT_H -// extern const char *CJOSE_HDR_ATTRS[]; +#include + +#include "cjose/header.h" + +bool _cjose_header_validate_crit(cjose_header_t *header, const char *const *supported, size_t supported_len, cjose_err *err); #endif // SRC_HEADER_INT_H diff --git a/src/include/util_int.h b/src/include/util_int.h index c8d059c..a550b93 100644 --- a/src/include/util_int.h +++ b/src/include/util_int.h @@ -24,4 +24,7 @@ void *cjose_alloc_wrapped(size_t n); void *cjose_realloc_wrapped(void *p, size_t n); void cjose_dealloc_wrapped(void *p); +void _cjose_cleanse(void *ptr, size_t len); +void _cjose_cleanse_dealloc(void *ptr, size_t len); + #endif // SRC_UTIL_INT_H diff --git a/src/jwe.c b/src/jwe.c index faed05b..98ff157 100644 --- a/src/jwe.c +++ b/src/jwe.c @@ -14,7 +14,9 @@ #include #include +#include #include +#include #include #include #include @@ -69,6 +71,12 @@ static bool _cjose_jwe_decrypt_dat_aes_gcm(cjose_jwe_t *jwe, cjose_err *err); static bool _cjose_jwe_decrypt_dat_aes_cbc(cjose_jwe_t *jwe, cjose_err *err); +static bool _cjose_jwe_validate_decrypt_key(_jwe_int_recipient_t *recipient, + cjose_header_t *protected_header, + cjose_header_t *shared_header, + const cjose_jwk_t *jwk, + cjose_err *err); + static void _cjose_release_cek(uint8_t **cek, size_t cek_len) { @@ -77,8 +85,7 @@ static void _cjose_release_cek(uint8_t **cek, size_t cek_len) return; } - memset(*cek, 0, cek_len); - cjose_get_dealloc()(*cek); + _cjose_cleanse_dealloc(*cek, cek_len); *cek = 0; } @@ -313,6 +320,24 @@ static bool _cjose_jwe_validate_alg(cjose_header_t *protected_header, _jwe_int_recipient_t *recipient, cjose_err *err) { + static const char *const supported_crit_headers[] = { + "alg", + "enc", + "cty", + "epk", + "apu", + "apv" + }; + + if (!_cjose_header_validate_crit(protected_header, supported_crit_headers, + sizeof(supported_crit_headers) / sizeof(supported_crit_headers[0]), err) + || !_cjose_header_validate_crit(unprotected_header, supported_crit_headers, + sizeof(supported_crit_headers) / sizeof(supported_crit_headers[0]), err) + || !_cjose_header_validate_crit((cjose_header_t *)recipient->unprotected, supported_crit_headers, + sizeof(supported_crit_headers) / sizeof(supported_crit_headers[0]), err)) + { + return false; + } const char *alg = _cjose_jwe_get_from_headers(protected_header, unprotected_header, (cjose_header_t *)recipient->unprotected, CJOSE_HDR_ALG); @@ -825,7 +850,7 @@ static bool _cjose_jwe_encrypt_ek_ecdh_es(_jwe_int_recipient_t *recipient, cjose cjose_jwk_release(epk_jwk); cjose_get_dealloc()(epk_json); - cjose_get_dealloc()(secret); + _cjose_cleanse_dealloc(secret, secret_len); cjose_get_dealloc()(otherinfo); return result; @@ -860,6 +885,12 @@ static bool _cjose_jwe_decrypt_ek_ecdh_es(_jwe_int_recipient_t *recipient, cjose goto cjose_decrypt_ek_ecdh_es_finish; } + if (cjose_jwk_EC_get_curve(jwk, err) != cjose_jwk_EC_get_curve(epk_jwk, err)) + { + CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG); + goto cjose_decrypt_ek_ecdh_es_finish; + } + // perform ECDH (private=jwk, public=epk_jwk) if (!cjose_jwk_derive_ecdh_bits(jwk, epk_jwk, &secret, &secret_len, err)) { @@ -904,7 +935,7 @@ static bool _cjose_jwe_decrypt_ek_ecdh_es(_jwe_int_recipient_t *recipient, cjose cjose_jwk_release(epk_jwk); cjose_get_dealloc()(epk_json); - cjose_get_dealloc()(secret); + _cjose_cleanse_dealloc(secret, secret_len); cjose_get_dealloc()(otherinfo); return result; @@ -944,7 +975,7 @@ static bool _cjose_jwe_set_iv_aes_cbc(cjose_jwe_t *jwe, cjose_err *err) // And in the example in A.2.4 (https://tools.ietf.org/html/rfc7516#appendix-A.2.4) // they provide an example for AES128-CBC, which results (naturally) in the IV size of 128Bit. // - // The CISCO implementation chooses for the size of the IV the key size of the + // The CISCO implementation chose for the size of the IV the key size of the // cipher algorithm, which seems to be wrong. // // According to RFC 3602 section 3 (https://tools.ietf.org/html/rfc3602#section-3): @@ -1128,7 +1159,16 @@ static bool _cjose_jwe_calc_auth_tag(const char *enc, cjose_jwe_t *jwe, uint8_t uint64_t al = jwe->enc_header.b64u_len * 8; // concatenate AAD + IV + ciphertext + AAD length field - int msg_len = jwe->enc_header.b64u_len + jwe->enc_iv.raw_len + jwe->enc_ct.raw_len + sizeof(uint64_t); + size_t msg_len = jwe->enc_header.b64u_len; + if (msg_len > SIZE_MAX - jwe->enc_iv.raw_len || msg_len + jwe->enc_iv.raw_len > SIZE_MAX - jwe->enc_ct.raw_len + || msg_len + jwe->enc_iv.raw_len + jwe->enc_ct.raw_len > SIZE_MAX - sizeof(uint64_t)) + { + CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG); + goto _cjose_jwe_calc_auth_tag_end; + } + msg_len += jwe->enc_iv.raw_len; + msg_len += jwe->enc_ct.raw_len; + msg_len += sizeof(uint64_t); if (!_cjose_jwe_malloc(msg_len, false, &msg, err)) { goto _cjose_jwe_calc_auth_tag_end; @@ -1319,6 +1359,12 @@ static bool _cjose_jwe_decrypt_dat_aes_gcm(cjose_jwe_t *jwe, cjose_err *err) } EVP_CIPHER_CTX_init(ctx); + if (jwe->enc_iv.raw_len != 12) + { + CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG); + goto _cjose_jwe_decrypt_dat_aes_gcm_fail; + } + // initialize context for decryption using AES GCM cipher and CEK and IV if (EVP_DecryptInit_ex(ctx, cipher, NULL, jwe->cek, jwe->enc_iv.raw) != 1) { @@ -1394,6 +1440,12 @@ static bool _cjose_jwe_decrypt_dat_aes_cbc(cjose_jwe_t *jwe, cjose_err *err) } const char *enc = json_string_value(enc_obj); + if (jwe->enc_iv.raw_len != AES_BLOCK_SIZE) + { + CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG); + return false; + } + // calculate Authentication Tag unsigned int tag_len = 0; uint8_t tag[EVP_MAX_MD_SIZE]; @@ -1403,7 +1455,7 @@ static bool _cjose_jwe_decrypt_dat_aes_cbc(cjose_jwe_t *jwe, cjose_err *err) } // compare the provided Authentication Tag against our calculation - if ((tag_len != jwe->enc_auth_tag.raw_len) || (cjose_const_memcmp(tag, jwe->enc_auth_tag.raw, tag_len) != 0)) + if ((tag_len != jwe->enc_auth_tag.raw_len) || (CRYPTO_memcmp(tag, jwe->enc_auth_tag.raw, tag_len) != 0)) { CJOSE_ERROR(err, CJOSE_ERR_CRYPTO); return false; @@ -1449,7 +1501,13 @@ static bool _cjose_jwe_decrypt_dat_aes_cbc(cjose_jwe_t *jwe, cjose_err *err) } // allocate buffer for the plaintext + one block padding - int p_len = jwe->enc_ct.raw_len, f_len = 0; + if (jwe->enc_ct.raw_len > INT_MAX || jwe->enc_ct.raw_len > SIZE_MAX - AES_BLOCK_SIZE) + { + CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG); + goto _cjose_jwe_decrypt_dat_aes_cbc_fail; + } + + int p_len = (int)jwe->enc_ct.raw_len, f_len = 0; cjose_get_dealloc()(jwe->dat); jwe->dat_len = p_len + AES_BLOCK_SIZE; if (!_cjose_jwe_malloc(jwe->dat_len, false, &jwe->dat, err)) @@ -1484,6 +1542,43 @@ static bool _cjose_jwe_decrypt_dat_aes_cbc(cjose_jwe_t *jwe, cjose_err *err) return false; } +//////////////////////////////////////////////////////////////////////////////// +static bool _cjose_jwe_validate_decrypt_key(_jwe_int_recipient_t *recipient, + cjose_header_t *protected_header, + cjose_header_t *shared_header, + const cjose_jwk_t *jwk, + cjose_err *err) +{ + const char *alg = _cjose_jwe_get_from_headers(protected_header, shared_header, (cjose_header_t *)recipient->unprotected, CJOSE_HDR_ALG); + if (NULL == alg) + { + CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG); + return false; + } + + if (((0 == strcmp(alg, CJOSE_HDR_ALG_RSA_OAEP)) || (0 == strcmp(alg, CJOSE_HDR_ALG_RSA1_5))) && jwk->kty != CJOSE_JWK_KTY_RSA) + { + CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG); + return false; + } + + if (((0 == strcmp(alg, CJOSE_HDR_ALG_A128KW)) || (0 == strcmp(alg, CJOSE_HDR_ALG_A192KW)) || (0 == strcmp(alg, CJOSE_HDR_ALG_A256KW)) + || (0 == strcmp(alg, CJOSE_HDR_ALG_DIR))) + && jwk->kty != CJOSE_JWK_KTY_OCT) + { + CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG); + return false; + } + + if ((0 == strcmp(alg, CJOSE_HDR_ALG_ECDH_ES)) && jwk->kty != CJOSE_JWK_KTY_EC) + { + CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG); + return false; + } + + return true; +} + //////////////////////////////////////////////////////////////////////////////// cjose_jwe_t *cjose_jwe_encrypt_iv(const cjose_jwk_t *jwk, cjose_header_t *protected_header, @@ -2084,6 +2179,11 @@ uint8_t *cjose_jwe_decrypt_multi(cjose_jwe_t *jwe, cjose_key_locator key_locator continue; } + if (!_cjose_jwe_validate_decrypt_key(jwe->to + i, (cjose_header_t *)jwe->hdr, (cjose_header_t *)jwe->shared_hdr, key, err)) + { + goto _cjose_jwe_decrypt_multi_fail; + } + // decrypt JWE content-encryption key from encrypted key if (!jwe->to[i].fns.decrypt_ek(jwe->to + i, jwe, key, err)) { @@ -2147,6 +2247,11 @@ uint8_t *cjose_jwe_decrypt(cjose_jwe_t *jwe, const cjose_jwk_t *jwk, size_t *con return NULL; } + if (!_cjose_jwe_validate_decrypt_key(jwe->to, (cjose_header_t *)jwe->hdr, (cjose_header_t *)jwe->shared_hdr, jwk, err)) + { + return NULL; + } + // decrypt JWE content-encryption key from encrypted key if (!jwe->to[0].fns.decrypt_ek(jwe->to, jwe, jwk, err)) { diff --git a/src/jwk.c b/src/jwk.c index 8dbdeec..b2068a8 100644 --- a/src/jwk.c +++ b/src/jwk.c @@ -13,6 +13,7 @@ #include #include +#include #include #include #include @@ -169,8 +170,13 @@ cjose_jwk_t *cjose_jwk_retain(cjose_jwk_t *jwk, cjose_err *err) return NULL; } + if (UINT_MAX == jwk->retained) + { + CJOSE_ERROR(err, CJOSE_ERR_INVALID_STATE); + return NULL; + } + ++(jwk->retained); - // TODO: check for overflow return jwk; } @@ -381,7 +387,7 @@ static void _oct_free(cjose_jwk_t *jwk) jwk->keydata = NULL; if (buffer) { - cjose_get_dealloc()(buffer); + _cjose_cleanse_dealloc(buffer, jwk->keysize / 8); } cjose_get_dealloc()(jwk); } @@ -944,7 +950,13 @@ cjose_jwk_t *cjose_jwk_create_EC_spec(const cjose_jwk_ec_keyspec *spec, cjose_er if (1 != EC_POINT_set_affine_coordinates_GFp(params, Q, bnX, bnY, NULL)) { - CJOSE_ERROR(err, CJOSE_ERR_NO_MEMORY); + CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG); + goto create_EC_failed; + } + + if (1 != EC_POINT_is_on_curve(params, Q, NULL)) + { + CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG); goto create_EC_failed; } } @@ -952,7 +964,13 @@ cjose_jwk_t *cjose_jwk_create_EC_spec(const cjose_jwk_ec_keyspec *spec, cjose_er // always set the public key if (1 != EC_KEY_set_public_key(ec, Q)) { - CJOSE_ERROR(err, CJOSE_ERR_NO_MEMORY); + CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG); + goto create_EC_failed; + } + + if (1 != EC_KEY_check_key(ec)) + { + CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG); goto create_EC_failed; } @@ -1734,8 +1752,8 @@ cjose_jwk_t *cjose_jwk_derive_ecdh_ephemeral_key( } // happy path - cjose_get_dealloc()(secret); - cjose_get_dealloc()(ephemeral_key); + _cjose_cleanse_dealloc(secret, secret_len); + _cjose_cleanse_dealloc(ephemeral_key, ephemeral_key_len); return jwk_ephemeral_key; @@ -1746,8 +1764,8 @@ cjose_jwk_t *cjose_jwk_derive_ecdh_ephemeral_key( { cjose_jwk_release(jwk_ephemeral_key); } - cjose_get_dealloc()(secret); - cjose_get_dealloc()(ephemeral_key); + _cjose_cleanse_dealloc(secret, secret_len); + _cjose_cleanse_dealloc(ephemeral_key, ephemeral_key_len); return NULL; } @@ -1803,7 +1821,7 @@ bool cjose_jwk_derive_ecdh_bits( // allocate buffer for shared secret secret = (uint8_t *)cjose_get_alloc()(secret_len); - if (NULL == output) + if (NULL == secret) { CJOSE_ERROR(err, CJOSE_ERR_NO_MEMORY); goto _cjose_jwk_derive_bits_fail; @@ -1840,7 +1858,7 @@ bool cjose_jwk_derive_ecdh_bits( { EVP_PKEY_free(pkey_peer); } - cjose_get_dealloc()(secret); + _cjose_cleanse_dealloc(secret, secret_len); return false; } @@ -1877,8 +1895,10 @@ bool cjose_jwk_hkdf(const EVP_MD *md, if (NULL == HMAC(md, prk, prk_len, t, sizeof(t), okm, NULL)) { CJOSE_ERROR(err, CJOSE_ERR_CRYPTO); + _cjose_cleanse(prk, sizeof(prk)); return false; } + _cjose_cleanse(prk, sizeof(prk)); return true; } diff --git a/src/jws.c b/src/jws.c index d0cfa0d..13bbb31 100644 --- a/src/jws.c +++ b/src/jws.c @@ -15,6 +15,7 @@ #include #include +#include #include #include #include @@ -22,6 +23,7 @@ #include "include/jwk_int.h" #include "include/header_int.h" #include "include/jws_int.h" +#include "include/util_int.h" //////////////////////////////////////////////////////////////////////////////// static bool _cjose_jws_build_dig_sha(cjose_jws_t *jws, const cjose_jwk_t *jwk, cjose_err *err); @@ -44,6 +46,8 @@ static bool _cjose_jws_build_sig_ec(cjose_jws_t *jws, const cjose_jwk_t *jwk, cj static bool _cjose_jws_verify_sig_ec(cjose_jws_t *jws, const cjose_jwk_t *jwk, cjose_err *err); +static bool _cjose_jws_validate_verify_key(cjose_jws_t *jws, const cjose_jwk_t *jwk, cjose_err *err); + //////////////////////////////////////////////////////////////////////////////// static bool _cjose_jws_build_hdr(cjose_jws_t *jws, cjose_header_t *header, cjose_err *err) { @@ -71,6 +75,17 @@ static bool _cjose_jws_build_hdr(cjose_jws_t *jws, cjose_header_t *header, cjose //////////////////////////////////////////////////////////////////////////////// static bool _cjose_jws_validate_hdr(cjose_jws_t *jws, cjose_err *err) { + static const char *const supported_crit_headers[] = { + "alg", + "cty" + }; + + if (!_cjose_header_validate_crit((cjose_header_t *)jws->hdr, supported_crit_headers, + sizeof(supported_crit_headers) / sizeof(supported_crit_headers[0]), err)) + { + return false; + } + // make sure we have an alg header json_t *alg_obj = json_object_get(jws->hdr, CJOSE_HDR_ALG); if ((NULL == alg_obj) || (!json_is_string(alg_obj))) @@ -174,7 +189,7 @@ static bool _cjose_jws_build_dig_sha(cjose_jws_t *jws, const cjose_jwk_t *jwk, c if (NULL != jws->dig) { - cjose_get_dealloc()(jws->dig); + _cjose_cleanse_dealloc(jws->dig, jws->dig_len); jws->dig = NULL; } @@ -720,8 +735,8 @@ void cjose_jws_release(cjose_jws_t *jws) cjose_get_dealloc()(jws->hdr_b64u); cjose_get_dealloc()(jws->dat); cjose_get_dealloc()(jws->dat_b64u); - cjose_get_dealloc()(jws->dig); - cjose_get_dealloc()(jws->sig); + _cjose_cleanse_dealloc(jws->dig, jws->dig_len); + _cjose_cleanse_dealloc(jws->sig, jws->sig_len); cjose_get_dealloc()(jws->sig_b64u); cjose_get_dealloc()(jws->cser); cjose_get_dealloc()(jws); @@ -749,9 +764,9 @@ bool cjose_jws_export(cjose_jws_t *jws, const char **compact, cjose_err *err) static bool _cjose_jws_strcpy(char **dst, const char *src, int len, cjose_err *err) { *dst = (char *)cjose_get_alloc()(len + 1); - if (NULL == dst) + if (NULL == *dst) { - CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG); + CJOSE_ERROR(err, CJOSE_ERR_NO_MEMORY); return false; } @@ -804,7 +819,11 @@ cjose_jws_t *cjose_jws_import(const char *cser, size_t cser_len, cjose_err *err) // copy and decode header b64u segment uint8_t *hdr_str = NULL; jws->hdr_b64u_len = d[0]; - _cjose_jws_strcpy(&jws->hdr_b64u, cser, jws->hdr_b64u_len, err); + if (!_cjose_jws_strcpy(&jws->hdr_b64u, cser, jws->hdr_b64u_len, err)) + { + cjose_jws_release(jws); + return NULL; + } if (!cjose_base64url_decode(jws->hdr_b64u, jws->hdr_b64u_len, &hdr_str, &len, err) || NULL == hdr_str) { cjose_jws_release(jws); @@ -843,7 +862,11 @@ cjose_jws_t *cjose_jws_import(const char *cser, size_t cser_len, cjose_err *err) // copy and b64u decode data segment jws->dat_b64u_len = d[1] - d[0] - 1; - _cjose_jws_strcpy(&jws->dat_b64u, cser + d[0] + 1, jws->dat_b64u_len, err); + if (!_cjose_jws_strcpy(&jws->dat_b64u, cser + d[0] + 1, jws->dat_b64u_len, err)) + { + cjose_jws_release(jws); + return NULL; + } if (!cjose_base64url_decode(jws->dat_b64u, jws->dat_b64u_len, &jws->dat, &jws->dat_len, err)) { cjose_jws_release(jws); @@ -852,7 +875,11 @@ cjose_jws_t *cjose_jws_import(const char *cser, size_t cser_len, cjose_err *err) // copy and b64u decode signature segment jws->sig_b64u_len = cser_len - d[1] - 1; - _cjose_jws_strcpy(&jws->sig_b64u, cser + d[1] + 1, jws->sig_b64u_len, err); + if (!_cjose_jws_strcpy(&jws->sig_b64u, cser + d[1] + 1, jws->sig_b64u_len, err)) + { + cjose_jws_release(jws); + return NULL; + } if (!cjose_base64url_decode(jws->sig_b64u, jws->sig_b64u_len, &jws->sig, &jws->sig_len, err)) { cjose_jws_release(jws); @@ -985,6 +1012,7 @@ static bool _cjose_jws_verify_sig_rs(cjose_jws_t *jws, const cjose_jwk_t *jwk, c static bool _cjose_jws_verify_sig_hmac_sha(cjose_jws_t *jws, const cjose_jwk_t *jwk, cjose_err *err) { bool retval = false; + int diff = 0; // ensure jwk is OCT if (jwk->kty != CJOSE_JWK_KTY_OCT) @@ -994,7 +1022,12 @@ static bool _cjose_jws_verify_sig_hmac_sha(cjose_jws_t *jws, const cjose_jwk_t * } // verify decrypted digest matches computed digest - if ((cjose_const_memcmp(jws->dig, jws->sig, jws->dig_len) != 0) || (jws->sig_len != jws->dig_len)) + diff |= (jws->sig_len != jws->dig_len); + if (jws->sig_len == jws->dig_len) + { + diff |= CRYPTO_memcmp(jws->dig, jws->sig, jws->dig_len); + } + if (diff != 0) { CJOSE_ERROR(err, CJOSE_ERR_CRYPTO); goto _cjose_jws_verify_sig_hmac_sha_cleanup; @@ -1052,6 +1085,49 @@ static bool _cjose_jws_verify_sig_ec(cjose_jws_t *jws, const cjose_jwk_t *jwk, c return retval; } +//////////////////////////////////////////////////////////////////////////////// +static bool _cjose_jws_validate_verify_key(cjose_jws_t *jws, const cjose_jwk_t *jwk, cjose_err *err) +{ + json_t *alg_obj = json_object_get(jws->hdr, CJOSE_HDR_ALG); + if (NULL == alg_obj || !json_is_string(alg_obj)) + { + CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG); + return false; + } + + const char *alg = json_string_value(alg_obj); + if (0 == strcmp(alg, CJOSE_HDR_ALG_NONE)) + { + CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG); + return false; + } + + if (((0 == strcmp(alg, CJOSE_HDR_ALG_PS256)) || (0 == strcmp(alg, CJOSE_HDR_ALG_PS384)) || (0 == strcmp(alg, CJOSE_HDR_ALG_PS512)) + || (0 == strcmp(alg, CJOSE_HDR_ALG_RS256)) || (0 == strcmp(alg, CJOSE_HDR_ALG_RS384)) + || (0 == strcmp(alg, CJOSE_HDR_ALG_RS512))) + && jwk->kty != CJOSE_JWK_KTY_RSA) + { + CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG); + return false; + } + + if (((0 == strcmp(alg, CJOSE_HDR_ALG_HS256)) || (0 == strcmp(alg, CJOSE_HDR_ALG_HS384)) || (0 == strcmp(alg, CJOSE_HDR_ALG_HS512))) + && jwk->kty != CJOSE_JWK_KTY_OCT) + { + CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG); + return false; + } + + if (((0 == strcmp(alg, CJOSE_HDR_ALG_ES256)) || (0 == strcmp(alg, CJOSE_HDR_ALG_ES384)) || (0 == strcmp(alg, CJOSE_HDR_ALG_ES512))) + && jwk->kty != CJOSE_JWK_KTY_EC) + { + CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG); + return false; + } + + return true; +} + //////////////////////////////////////////////////////////////////////////////// bool cjose_jws_verify(cjose_jws_t *jws, const cjose_jwk_t *jwk, cjose_err *err) { @@ -1067,6 +1143,11 @@ bool cjose_jws_verify(cjose_jws_t *jws, const cjose_jwk_t *jwk, cjose_err *err) return false; } + if (!_cjose_jws_validate_verify_key(jws, jwk, err)) + { + return false; + } + // build JWS digest from header and payload (hashed signing input value) if (!jws->fns.digest(jws, jwk, err)) { diff --git a/src/util.c b/src/util.c index a180d11..4c6bb79 100644 --- a/src/util.c +++ b/src/util.c @@ -92,15 +92,26 @@ cjose_realloc3_fn_t cjose_get_realloc3(void) { return (!_realloc3) ? cjose_reall cjose_dealloc_fn_t cjose_get_dealloc(void) { return (!_dealloc) ? free : _dealloc; } cjose_dealloc3_fn_t cjose_get_dealloc3(void) { return (!_dealloc3) ? cjose_dealloc3_default : _dealloc3; } -int cjose_const_memcmp(const uint8_t *a, const uint8_t *b, const size_t size) +void _cjose_cleanse(void *ptr, size_t len) { - unsigned char result = 0; - for (size_t i = 0; i < size; i++) + if (NULL != ptr && 0 != len) { - result |= a[i] ^ b[i]; + OPENSSL_cleanse(ptr, len); } +} - return result; +void _cjose_cleanse_dealloc(void *ptr, size_t len) +{ + if (NULL != ptr) + { + _cjose_cleanse(ptr, len); + cjose_get_dealloc()(ptr); + } +} + +int cjose_const_memcmp(const uint8_t *a, const uint8_t *b, const size_t size) +{ + return CRYPTO_memcmp(a, b, size); } char *_cjose_strndup(const char *str, ssize_t len, cjose_err *err)