Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

support for symmetric key JWA HMAC algorithms HS256, HS384 and HS512 #8

Merged
merged 1 commit into from
Jul 16, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions include/cjose/header.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,11 @@ extern const char *CJOSE_HDR_ALG_PS256;
/** The JWE algorithm attribute value for RS256. */
extern const char *CJOSE_HDR_ALG_RS256;

/** The JWE algorithm attribute values for HS256, HS384 and HS512. */
extern const char *CJOSE_HDR_ALG_HS256;
extern const char *CJOSE_HDR_ALG_HS384;
extern const char *CJOSE_HDR_ALG_HS512;

/** The JWE algorithm attribute value for "dir". */
extern const char *CJOSE_HDR_ALG_DIR;

Expand Down
3 changes: 3 additions & 0 deletions src/header.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ const char *CJOSE_HDR_ALG_RSA_OAEP = "RSA-OAEP";
const char *CJOSE_HDR_ALG_DIR = "dir";
const char *CJOSE_HDR_ALG_PS256 = "PS256";
const char *CJOSE_HDR_ALG_RS256 = "RS256";
const char *CJOSE_HDR_ALG_HS256 = "HS256";
const char *CJOSE_HDR_ALG_HS384 = "HS384";
const char *CJOSE_HDR_ALG_HS512 = "HS512";

const char *CJOSE_HDR_ENC = "enc";
const char *CJOSE_HDR_ENC_A256GCM = "A256GCM";
Expand Down
174 changes: 174 additions & 0 deletions src/jws.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include <openssl/evp.h>
#include <openssl/rsa.h>
#include <openssl/err.h>
#include <openssl/hmac.h>

#include "include/jwk_int.h"
#include "include/header_int.h"
Expand All @@ -34,6 +35,11 @@ static bool _cjose_jws_build_sig_ps256(
const cjose_jwk_t *jwk,
cjose_err *err);

static bool _cjose_jws_build_dig_hmac_sha(
cjose_jws_t *jws,
const cjose_jwk_t *jwk,
cjose_err *err);

static bool _cjose_jws_verify_sig_ps256(
cjose_jws_t *jws,
const cjose_jwk_t *jwk,
Expand All @@ -49,6 +55,15 @@ static bool _cjose_jws_verify_sig_rs256(
const cjose_jwk_t *jwk,
cjose_err *err);

static bool _cjose_jws_build_sig_hmac_sha(
cjose_jws_t *jws,
const cjose_jwk_t *jwk,
cjose_err *err);

static bool _cjose_jws_verify_sig_hmac_sha(
cjose_jws_t *jws,
const cjose_jwk_t *jwk,
cjose_err *err);

////////////////////////////////////////////////////////////////////////////////
static bool _cjose_jws_build_hdr(
Expand Down Expand Up @@ -105,6 +120,12 @@ static bool _cjose_jws_validate_hdr(
jws->fns.sign = _cjose_jws_build_sig_rs256;
jws->fns.verify = _cjose_jws_verify_sig_rs256;
}
else if ( (strcmp(alg, CJOSE_HDR_ALG_HS256) == 0) || (strcmp(alg, CJOSE_HDR_ALG_HS384) == 0) || (strcmp(alg, CJOSE_HDR_ALG_HS512) == 0))
{
jws->fns.digest = _cjose_jws_build_dig_hmac_sha;
jws->fns.sign = _cjose_jws_build_sig_hmac_sha;
jws->fns.verify = _cjose_jws_verify_sig_hmac_sha;
}
else
{
CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG);
Expand Down Expand Up @@ -217,6 +238,96 @@ static bool _cjose_jws_build_dig_sha256(
return retval;
}

////////////////////////////////////////////////////////////////////////////////
static bool _cjose_jws_build_dig_hmac_sha(
cjose_jws_t *jws,
const cjose_jwk_t *jwk,
cjose_err *err)
{
bool retval = false;
HMAC_CTX *ctx = NULL;

// make sure we have an alg header
json_t *alg_obj = json_object_get(jws->hdr, CJOSE_HDR_ALG);
if (NULL == alg_obj)
{
CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG);
return false;
}
const char *alg = json_string_value(alg_obj);

// build digest using SHA-??? digest algorithm
const EVP_MD *digest_alg = NULL;
if (strcmp(alg, CJOSE_HDR_ALG_HS256) == 0)
digest_alg = EVP_sha256();
else if (strcmp(alg, CJOSE_HDR_ALG_HS384) == 0)
digest_alg = EVP_sha384();
else if (strcmp(alg, CJOSE_HDR_ALG_HS512) == 0)
digest_alg = EVP_sha512();

if (NULL == digest_alg)
{
CJOSE_ERROR(err, CJOSE_ERR_CRYPTO);
goto _cjose_jws_build_dig_hmac_sha_cleanup;
}

// allocate buffer for digest
jws->dig_len = digest_alg->md_size;
jws->dig = (uint8_t *)cjose_get_alloc()(jws->dig_len);
if (NULL == jws->dig)
{
CJOSE_ERROR(err, CJOSE_ERR_NO_MEMORY);
goto _cjose_jws_build_dig_hmac_sha_cleanup;
}

// instantiate and initialize a new mac digest context
ctx = cjose_get_alloc()(sizeof(HMAC_CTX));
if (NULL == ctx)
{
CJOSE_ERROR(err, CJOSE_ERR_CRYPTO);
goto _cjose_jws_build_dig_hmac_sha_cleanup;
}
HMAC_CTX_init(ctx);

// create digest as DIGEST(B64U(HEADER).B64U(DATA))
if (HMAC_Init_ex(ctx, jwk->keydata, jwk->keysize / 8, digest_alg, NULL) != 1)
{
CJOSE_ERROR(err, CJOSE_ERR_CRYPTO);
goto _cjose_jws_build_dig_hmac_sha_cleanup;
}
if (HMAC_Update(ctx, (const unsigned char *)jws->hdr_b64u, jws->hdr_b64u_len) != 1)
{
CJOSE_ERROR(err, CJOSE_ERR_CRYPTO);
goto _cjose_jws_build_dig_hmac_sha_cleanup;
}
if (HMAC_Update(ctx, (const unsigned char *)".", 1) != 1)
{
CJOSE_ERROR(err, CJOSE_ERR_CRYPTO);
goto _cjose_jws_build_dig_hmac_sha_cleanup;
}
if (HMAC_Update(ctx, (const unsigned char *)jws->dat_b64u, jws->dat_b64u_len) != 1)
{
CJOSE_ERROR(err, CJOSE_ERR_CRYPTO);
goto _cjose_jws_build_dig_hmac_sha_cleanup;
}
if (HMAC_Final(ctx, jws->dig, NULL) != 1)
{
CJOSE_ERROR(err, CJOSE_ERR_CRYPTO);
goto _cjose_jws_build_dig_hmac_sha_cleanup;
}

// if we got this far - success
retval = true;

_cjose_jws_build_dig_hmac_sha_cleanup:
if (NULL != ctx)
{
HMAC_CTX_cleanup(ctx);
cjose_get_dealloc()(ctx);
}

return retval;
}

////////////////////////////////////////////////////////////////////////////////
static bool _cjose_jws_build_sig_ps256(
Expand Down Expand Up @@ -333,6 +444,39 @@ static bool _cjose_jws_build_sig_rs256(
return true;
}

static bool _cjose_jws_build_sig_hmac_sha(
cjose_jws_t *jws,
const cjose_jwk_t *jwk,
cjose_err *err)
{
// ensure jwk is OCT
if (jwk->kty != CJOSE_JWK_KTY_OCT)
{
CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG);
return false;
}

// allocate buffer for signature
jws->sig_len = jws->dig_len;
jws->sig = (uint8_t *)cjose_get_alloc()(jws->sig_len);
if (NULL == jws->sig)
{
CJOSE_ERROR(err, CJOSE_ERR_NO_MEMORY);
return false;
}

memcpy(jws->sig, jws->dig, jws->sig_len);

// base64url encode signed digest
if (!cjose_base64url_encode((const uint8_t *)jws->sig, jws->sig_len,
&jws->sig_b64u, &jws->sig_b64u_len, err))
{
CJOSE_ERROR(err, CJOSE_ERR_CRYPTO);
return false;
}

return true;
}

////////////////////////////////////////////////////////////////////////////////
static bool _cjose_jws_build_cser(
Expand Down Expand Up @@ -728,6 +872,36 @@ static bool _cjose_jws_verify_sig_rs256(
return retval;
}

////////////////////////////////////////////////////////////////////////////////
static bool _cjose_jws_verify_sig_hmac_sha(
cjose_jws_t *jws,
const cjose_jwk_t *jwk,
cjose_err *err)
{
bool retval = false;

// ensure jwk is OCT
if (jwk->kty != CJOSE_JWK_KTY_OCT)
{
CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG);
goto _cjose_jws_verify_sig_hmac_sha_cleanup;
}

// verify decrypted digest matches computed digest
if ((_const_memcmp(jws->dig, jws->sig, jws->dig_len) != 0) ||
(jws->sig_len != jws->dig_len))
{
CJOSE_ERROR(err, CJOSE_ERR_CRYPTO);
goto _cjose_jws_verify_sig_hmac_sha_cleanup;
}

// if we got this far - success
retval = true;

_cjose_jws_verify_sig_hmac_sha_cleanup:

return retval;
}

////////////////////////////////////////////////////////////////////////////////
bool cjose_jws_verify(
Expand Down
26 changes: 25 additions & 1 deletion test/check_jws.c
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ static const char *JWK_COMMON =
"\"dq\": \"ZWB_5qJENrKO39aBW-Jf-_twihUPVi50oarRWml_iP40pVP01HDTqyiMut2tf6pUQGdF-nqulG2Mopei6Ell5wItf7s_bmnHPYysBuMrtov5PuknfVD7UqeEp25nZuZzF4aflyhovV29B-bM-_8CS0OIGb6TeTC5T5SflY17UNE\", "
"\"qi\": \"RowmdelfiEBdqfBCSb3yblUKhwJsbyg6HtcugIVOC1yDxD5sZ0cjJPnXj7TJkrC0tICQ50MlPY5F650D9pvACIYnvrGEwsq757Lxg5nqshvuSC-7i1TMkv7_uPBmIxRfzqsnh_hVhxLgSUW1NI6_ncwk9vDQqpkY6qBirgvbyO0\" }";

static const char *JWK_COMMON_OCT =
"{ \"kty\": \"oct\", "
"\"k\": \"AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow\" }";

// a JWS encrypted with the above JWK_COMMON key
static const char *JWS_COMMON =
"eyAiYWxnIjogIlBTMjU2IiB9.SWYgeW91IHJldmVhbCB5b3VyIHNlY3JldHMgdG8gdGhlIHdpbmQsIHlvdSBzaG91bGQgbm90IGJsYW1lIHRoZSB3aW5kIGZvciByZXZlYWxpbmcgdGhlbSB0byB0aGUgdHJlZXMuIOKAlCBLYWhsaWwgR2licmFu.0YJo4r9gbI2nZ2_1_KLTY3i5SRcZvahRuToavqBvLbm87pN7IYx8YV9kwKQclMW2ASpbEAzKNIJfQ3FycobRwZGtqCI9sRUo0vQvkpb3HIS6HKp3Kvur57J7LcZhz7uNIxzUYNQSg4EWpwhF9FnGng7bmU8qjNPiXCWfQ-n74gopAVzd3KDJ5ai7q66voRc9pCKJVbsaIMHIqcl9OPiMdY5Hz3_PgBalR2632HOdpUlIMvnMOL3EQICvyBwxaYPbhMcCpEc3_4K-sywOGiCSp9KlaLcRq0knZtAT0ynJszaiOwfR-W18PEFLfGclpeR6e_gop9mq69t36wK7KRUjrQ";
Expand All @@ -37,10 +41,18 @@ static const char *PLAIN_COMMON =
"wind for revealing them to the trees. — Kahlil Gibran";


static const char *_self_get_jwt_by_alg(const char *alg) {
if ((strcmp(alg, CJOSE_HDR_ALG_HS256) == 0) || (strcmp(alg, CJOSE_HDR_ALG_HS384) == 0) || (strcmp(alg, CJOSE_HDR_ALG_HS512) == 0))
return JWK_COMMON_OCT;
return JWK_COMMON;
}

static void _self_sign_self_verify(
const char *plain1, const char *alg, cjose_err *err)
{
cjose_jwk_t *jwk = cjose_jwk_import(JWK_COMMON, strlen(JWK_COMMON), err);
const char *s_jwk = _self_get_jwt_by_alg(alg);
cjose_jwk_t *jwk = cjose_jwk_import(s_jwk, strlen(s_jwk), err);

ck_assert_msg(NULL != jwk, "cjose_jwk_import failed: "
"%s, file: %s, function: %s, line: %ld",
err->message, err->file, err->function, err->line);
Expand Down Expand Up @@ -113,6 +125,9 @@ START_TEST(test_cjose_jws_self_sign_self_verify)
cjose_err err;
_self_sign_self_verify(PLAIN_COMMON, CJOSE_HDR_ALG_PS256, &err);
_self_sign_self_verify(PLAIN_COMMON, CJOSE_HDR_ALG_RS256, &err);
_self_sign_self_verify(PLAIN_COMMON, CJOSE_HDR_ALG_HS256, &err);
_self_sign_self_verify(PLAIN_COMMON, CJOSE_HDR_ALG_HS384, &err);
_self_sign_self_verify(PLAIN_COMMON, CJOSE_HDR_ALG_HS512, &err);
}
END_TEST

Expand All @@ -122,6 +137,9 @@ START_TEST(test_cjose_jws_self_sign_self_verify_short)
cjose_err err;
_self_sign_self_verify("Setec Astronomy", CJOSE_HDR_ALG_PS256, &err);
_self_sign_self_verify("Setec Astronomy", CJOSE_HDR_ALG_RS256, &err);
_self_sign_self_verify("Setec Astronomy", CJOSE_HDR_ALG_HS256, &err);
_self_sign_self_verify("Setec Astronomy", CJOSE_HDR_ALG_HS384, &err);
_self_sign_self_verify("Setec Astronomy", CJOSE_HDR_ALG_HS512, &err);
}
END_TEST

Expand All @@ -131,6 +149,9 @@ START_TEST(test_cjose_jws_self_sign_self_verify_empty)
cjose_err err;
_self_sign_self_verify("", CJOSE_HDR_ALG_PS256, &err);
_self_sign_self_verify("", CJOSE_HDR_ALG_RS256, &err);
_self_sign_self_verify("", CJOSE_HDR_ALG_HS256, &err);
_self_sign_self_verify("", CJOSE_HDR_ALG_HS384, &err);
_self_sign_self_verify("", CJOSE_HDR_ALG_HS512, &err);
}
END_TEST

Expand All @@ -148,6 +169,9 @@ START_TEST(test_cjose_jws_self_sign_self_verify_many)
plain[len-1] = 0;
_self_sign_self_verify(plain, CJOSE_HDR_ALG_PS256, &err);
_self_sign_self_verify(plain, CJOSE_HDR_ALG_RS256, &err);
_self_sign_self_verify(plain, CJOSE_HDR_ALG_HS256, &err);
_self_sign_self_verify(plain, CJOSE_HDR_ALG_HS384, &err);
_self_sign_self_verify(plain, CJOSE_HDR_ALG_HS512, &err);
free(plain);
}
}
Expand Down