Skip to content

Commit

Permalink
Update: support symmetric HMAC "signatures"
Browse files Browse the repository at this point in the history
support for symmetric key JWA HMAC algorithms HS256, HS384 and HS512
  • Loading branch information
linuxwolf authored Jul 16, 2016
2 parents 29390a3 + 8901648 commit f43f17d
Show file tree
Hide file tree
Showing 4 changed files with 207 additions and 1 deletion.
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

0 comments on commit f43f17d

Please sign in to comment.