From 6d1d1be838b546cb73f8d24c42a681a0a0e1ec03 Mon Sep 17 00:00:00 2001 From: "Matthew A. Miller" Date: Tue, 5 Apr 2016 17:16:20 -0300 Subject: [PATCH] UPDATE: Expose protected header from imported/created JWE and JWS --- include/cjose/header.h | 11 +++++++++++ include/cjose/jwe.h | 16 ++++++++++++++-- include/cjose/jws.h | 16 ++++++++++++++-- src/header.c | 10 ++++++++++ src/include/jwe_int.h | 2 ++ src/jwe.c | 42 +++++++++++++++++++++++++++++------------- src/jws.c | 18 +++++++++++++++--- test/check_header.c | 16 ++++++++++++++++ test/check_jwe.c | 4 ++++ test/check_jws.c | 8 +++++++- 10 files changed, 122 insertions(+), 21 deletions(-) diff --git a/include/cjose/header.h b/include/cjose/header.h index f5a78c4..56ea405 100644 --- a/include/cjose/header.h +++ b/include/cjose/header.h @@ -71,6 +71,17 @@ cjose_header_t *cjose_header_new( cjose_err *err); +/** + * Retains an existing header object. Callers must use this method if the + * header will be used past the scope it was created in (e.g., from a + * `cjose_jws_t` object). + * + * \param header[in] the header object to be retained. + * \returns the retained header object + */ +cjose_header_t *cjose_header_retain( + cjose_header_t *header); + /** * Releases an existing header object. Callers must use this method * to dispose of header rather than directly free'ing a cjose_header diff --git a/include/cjose/jwe.h b/include/cjose/jwe.h index 7bb856b..45060a5 100644 --- a/include/cjose/jwe.h +++ b/include/cjose/jwe.h @@ -45,7 +45,7 @@ typedef struct _cjose_jwe_int cjose_jwe_t; * (e.g. (dir), the provided JWK must be symmetric (e.g. oct). * * \param jwk [in] the key to use for encrypting the JWE. - * \param header [in] additional header values to include in the JWE header. + * \param protected_header [in] additional header values to include in the JWE header. * \param plaintext [in] the plaintext to be encrypted in the JWE payload. * \param plaintext_len [in] the length of the plaintext. * \param err [out] An optional error object which can be used to get additional @@ -54,7 +54,7 @@ typedef struct _cjose_jwe_int cjose_jwe_t; */ cjose_jwe_t *cjose_jwe_encrypt( const cjose_jwk_t *jwk, - cjose_header_t *header, + cjose_header_t *protected_header, const uint8_t *plaintext, size_t plaintext_len, cjose_err *err); @@ -115,6 +115,18 @@ uint8_t *cjose_jwe_decrypt( cjose_err *err); +/** + * Returns the protected header of the JWE object. + * + * **NOTE:** The returned header is still owned by the JWE object. Users must + * call `cjose_header_retain()` if it is expected to be valid after the + * owning `cjose_jwe_t` is released. + * + * \param jwe [in] the JWE object for which the protected header is requested. + * \returns the (parsed) protected header + */ +cjose_header_t *cjose_jwe_get_protected(cjose_jwe_t *jwe); + /** * Releases the given JWE object. * diff --git a/include/cjose/jws.h b/include/cjose/jws.h index ba073da..4161563 100644 --- a/include/cjose/jws.h +++ b/include/cjose/jws.h @@ -38,7 +38,7 @@ typedef struct _cjose_jws_int cjose_jws_t; * and JWK. * * \param jwk [in] the key to use for signing the JWS. - * \param header [in] header values to include in the JWS header. + * \param protected_header [in] header values to include in the JWS header. * \param plaintext [in] the plaintext to be signed as the JWS payload. * \param plaintext_len [in] the length of the plaintext. * \param err [out] An optional error object which can be used to get additional @@ -47,7 +47,7 @@ typedef struct _cjose_jws_int cjose_jws_t; */ cjose_jws_t *cjose_jws_sign( const cjose_jwk_t *jwk, - cjose_header_t *header, + cjose_header_t *protected_header, const uint8_t *plaintext, size_t plaintext_len, cjose_err *err); @@ -128,6 +128,18 @@ bool cjose_jws_get_plaintext( cjose_err *err); +/** + * Returns the protected header of the JWS payload. + * + * **NOTE:** The returned header is still owned by the JWS object. Users must + * call `cjose_header_retain()` if it is expected to be valid after the + * owning `cjose_jws_t` is released. + * + * \param jws [in] the JWS object for which the protected header is requested. + * \returns the (parsed) protected header + */ +cjose_header_t *cjose_jws_get_protected(cjose_jws_t *jws); + /** * Releases the given JWS object. * diff --git a/src/header.c b/src/header.c index 02e8dfe..35c99f7 100644 --- a/src/header.c +++ b/src/header.c @@ -37,6 +37,16 @@ cjose_header_t *cjose_header_new( return retval; } +//////////////////////////////////////////////////////////////////////////////// +cjose_header_t *cjose_header_retain( + cjose_header_t *header) +{ + if (NULL != header) + { + header = json_incref(header); + } + return header; +} //////////////////////////////////////////////////////////////////////////////// void cjose_header_release( diff --git a/src/include/jwe_int.h b/src/include/jwe_int.h index e1a9130..11785f0 100644 --- a/src/include/jwe_int.h +++ b/src/include/jwe_int.h @@ -61,6 +61,8 @@ typedef struct _jwe_fntable_int // JWE object struct _cjose_jwe_int { + json_t *hdr; // header JSON object + struct _cjose_jwe_part_int part[5]; // the 5 JWE parts uint8_t *cek; // content-encryption key diff --git a/src/jwe.c b/src/jwe.c index fe44822..522483d 100644 --- a/src/jwe.c +++ b/src/jwe.c @@ -79,7 +79,7 @@ static bool _cjose_jwe_malloc( { if (RAND_bytes((unsigned char *)*buffer, bytes) != 1) { - free(*buffer); + cjose_get_dealloc()(*buffer); CJOSE_ERROR(err, CJOSE_ERR_CRYPTO); return false; } @@ -98,6 +98,10 @@ static bool _cjose_jwe_build_hdr( cjose_header_t *header, cjose_err *err) { + // save header object as part of the JWE (and incr. refcount) + jwe->hdr = header; + json_incref(jwe->hdr); + // serialize the header char *hdr_str = json_dumps(header, JSON_ENCODE_ANY | JSON_PRESERVE_ORDER); if (NULL == hdr_str) @@ -569,14 +573,14 @@ static bool _cjose_jwe_decrypt_dat_a256gcm( //////////////////////////////////////////////////////////////////////////////// cjose_jwe_t *cjose_jwe_encrypt( const cjose_jwk_t *jwk, - cjose_header_t *header, + cjose_header_t *protected_header, const uint8_t *plaintext, size_t plaintext_len, cjose_err *err) { cjose_jwe_t *jwe = NULL; - if (NULL == jwk || NULL == header) + if (NULL == jwk || NULL == protected_header) { CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG); return NULL; @@ -585,7 +589,7 @@ cjose_jwe_t *cjose_jwe_encrypt( // if not already set, add kid header to JWE to match that of JWK const char *kid = cjose_jwk_get_kid(jwk, err); if (NULL != kid) { - if (!cjose_header_set(header, CJOSE_HDR_KID, kid, err)) + if (!cjose_header_set(protected_header, CJOSE_HDR_KID, kid, err)) { CJOSE_ERROR(err, CJOSE_ERR_INVALID_STATE); return false; @@ -599,14 +603,14 @@ cjose_jwe_t *cjose_jwe_encrypt( } // validate JWE header - if (!_cjose_jwe_validate_hdr(jwe, header, err)) + if (!_cjose_jwe_validate_hdr(jwe, protected_header, err)) { cjose_jwe_release(jwe); return NULL; } // build JWE header - if (!_cjose_jwe_build_hdr(jwe, header, err)) + if (!_cjose_jwe_build_hdr(jwe, protected_header, err)) { cjose_jwe_release(jwe); return NULL; @@ -645,6 +649,11 @@ void cjose_jwe_release( { return; } + if (NULL != jwe->hdr) + { + json_decref(jwe->hdr); + } + for (int i = 0; i < 5; ++i) { cjose_get_dealloc()(jwe->part[i].raw); @@ -655,7 +664,6 @@ void cjose_jwe_release( cjose_get_dealloc()(jwe); } - //////////////////////////////////////////////////////////////////////////////// char *cjose_jwe_export( cjose_jwe_t *jwe, @@ -797,9 +805,9 @@ cjose_jwe_t *cjose_jwe_import( } // deserialize JSON header - json_t *header = json_loadb( - (const char *)jwe->part[0].raw, jwe->part[0].raw_len, 0, NULL); - if (NULL == header) + jwe->hdr = json_loadb( + (const char *)jwe->part[0].raw, jwe->part[0].raw_len, 0, NULL); + if (NULL == jwe->hdr) { CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG); cjose_jwe_release(jwe); @@ -807,14 +815,12 @@ cjose_jwe_t *cjose_jwe_import( } // validate the JSON header - if (!_cjose_jwe_validate_hdr(jwe, header, err)) + if (!_cjose_jwe_validate_hdr(jwe, jwe->hdr, err)) { - json_decref(header); CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG); cjose_jwe_release(jwe); return NULL; } - json_decref(header); return jwe; } @@ -853,3 +859,13 @@ uint8_t *cjose_jwe_decrypt( return content; } + +//////////////////////////////////////////////////////////////////////////////// +cjose_header_t *cjose_jwe_get_protected(cjose_jwe_t *jwe) +{ + if (NULL == jwe) + { + return NULL; + } + return jwe->hdr; +} diff --git a/src/jws.c b/src/jws.c index 31d7a14..d64e78f 100644 --- a/src/jws.c +++ b/src/jws.c @@ -371,14 +371,14 @@ static bool _cjose_jws_build_cser( //////////////////////////////////////////////////////////////////////////////// cjose_jws_t *cjose_jws_sign( const cjose_jwk_t *jwk, - cjose_header_t *header, + cjose_header_t *protected_header, const uint8_t *plaintext, size_t plaintext_len, cjose_err *err) { cjose_jws_t *jws = NULL; - if (NULL == jwk || NULL == header || NULL == plaintext) + if (NULL == jwk || NULL == protected_header || NULL == plaintext) { CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG); return NULL; @@ -394,7 +394,7 @@ cjose_jws_t *cjose_jws_sign( memset(jws, 0, sizeof(cjose_jws_t)); // build JWS header - if (!_cjose_jws_build_hdr(jws, header, err)) + if (!_cjose_jws_build_hdr(jws, protected_header, err)) { cjose_jws_release(jws); return NULL; @@ -784,3 +784,15 @@ bool cjose_jws_get_plaintext( return true; } + +//////////////////////////////////////////////////////////////////////////////// +cjose_header_t *cjose_jws_get_protected( + cjose_jws_t *jws) +{ + if (NULL == jws) + { + return NULL; + } + + return jws->hdr; +} diff --git a/test/check_header.c b/test/check_header.c index 7ffa660..113bd77 100644 --- a/test/check_header.c +++ b/test/check_header.c @@ -28,6 +28,21 @@ START_TEST(test_cjose_header_new_release) } END_TEST +START_TEST(test_cjose_header_retain_release) +{ + cjose_err err; + + cjose_header_t *header = cjose_header_new(&err); + ck_assert_msg(NULL != header, "cjose_header_new failed"); + + header = cjose_header_retain(header); + ck_assert_msg(NULL != header, "cjose_header_retain failed"); + + cjose_header_release(header); + + cjose_header_release(header); +} +END_TEST START_TEST(test_cjose_header_set_get) { @@ -70,6 +85,7 @@ Suite *cjose_header_suite() TCase *tc_header = tcase_create("core"); tcase_add_test(tc_header, test_cjose_header_new_release); + tcase_add_test(tc_header, test_cjose_header_retain_release); tcase_add_test(tc_header, test_cjose_header_set_get); suite_add_tcase(suite, tc_header); diff --git a/test/check_jwe.c b/test/check_jwe.c index 8c9e81f..fcb8b1c 100644 --- a/test/check_jwe.c +++ b/test/check_jwe.c @@ -117,6 +117,7 @@ static void _self_encrypt_self_decrypt_with_key( ck_assert_msg(NULL != jwe1, "cjose_jwe_encrypt failed: %s, file: %s, function: %s, line: %ld", err.message, err.file, err.function, err.line); + ck_assert(hdr == cjose_jwe_get_protected(jwe1)); // get the compact serialization of JWE char *compact = cjose_jwe_export(jwe1, &err); @@ -140,6 +141,9 @@ static void _self_encrypt_self_decrypt_with_key( err.message, err.file, err.function, err.line); // confirm plain2 == plain1 + ck_assert(json_equal( + cjose_jwe_get_protected(jwe1), + cjose_jwe_get_protected(jwe2))); ck_assert_msg( plain2_len == strlen(plain1), "length of decrypted plaintext does not match length of original, " diff --git a/test/check_jws.c b/test/check_jws.c index 2a30a64..3fe6241 100644 --- a/test/check_jws.c +++ b/test/check_jws.c @@ -59,6 +59,7 @@ static void _self_sign_self_verify( ck_assert_msg(NULL != jws1, "cjose_jws_sign failed: " "%s, file: %s, function: %s, line: %ld", err->message, err->file, err->function, err->line); + ck_assert(hdr == cjose_jws_get_protected(jws1)); // get the compact serialization of JWS char *compact = NULL; @@ -77,7 +78,7 @@ static void _self_sign_self_verify( // verify the deserialized JWS ck_assert_msg(cjose_jws_verify(jws2, jwk, err), "cjose_jws_verify failed"); - // get the verifyed plaintext + // get the verified plaintext uint8_t *plain2 = NULL; size_t plain2_len = 0; ck_assert_msg( @@ -86,6 +87,11 @@ static void _self_sign_self_verify( "%s, file: %s, function: %s, line: %ld", err->message, err->file, err->function, err->line); + // confirm equal headers + ck_assert(json_equal( + cjose_jws_get_protected(jws1), + cjose_jws_get_protected(jws2))); + // confirm plain2 == plain1 ck_assert_msg( plain2_len == strlen(plain1),