static bool _cjose_jws_verify_sig_ps(cjose_jws_t *jws, const cjose_jwk_t *jwk, cjose_err *err) { bool retval = false; uint8_t *em = NULL; size_t em_len = 0; // ensure jwk is RSA if (jwk->kty != CJOSE_JWK_KTY_RSA) { CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG); goto _cjose_jws_verify_sig_ps_cleanup; } // 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-256/384/512 digest algorithm const EVP_MD *digest_alg = NULL; if (strcmp(alg, CJOSE_HDR_ALG_PS256) == 0) digest_alg = EVP_sha256(); else if (strcmp(alg, CJOSE_HDR_ALG_PS384) == 0) digest_alg = EVP_sha384(); else if (strcmp(alg, CJOSE_HDR_ALG_PS512) == 0) digest_alg = EVP_sha512(); if (NULL == digest_alg) { CJOSE_ERROR(err, CJOSE_ERR_CRYPTO); goto _cjose_jws_verify_sig_ps_cleanup; } // allocate buffer for encoded message em_len = RSA_size((RSA *)jwk->keydata); em = (uint8_t *)cjose_get_alloc()(em_len); if (NULL == em) { CJOSE_ERROR(err, CJOSE_ERR_CRYPTO); goto _cjose_jws_verify_sig_ps_cleanup; } // decrypt signature if (RSA_public_decrypt(jws->sig_len, jws->sig, em, (RSA *)jwk->keydata, RSA_NO_PADDING) != em_len) { CJOSE_ERROR(err, CJOSE_ERR_CRYPTO); goto _cjose_jws_verify_sig_ps_cleanup; } // verify decrypted signature data against PSS encoded digest if (RSA_verify_PKCS1_PSS((RSA *)jwk->keydata, jws->dig, digest_alg, em, -1) != 1) { CJOSE_ERROR(err, CJOSE_ERR_CRYPTO); goto _cjose_jws_verify_sig_ps_cleanup; } // if we got this far - success retval = true; _cjose_jws_verify_sig_ps_cleanup: cjose_get_dealloc()(em); return retval; }
static bool _cjose_jwe_malloc( size_t bytes, bool random, uint8_t **buffer, cjose_err *err) { *buffer = (uint8_t *)cjose_get_alloc()(bytes); if (NULL == *buffer) { CJOSE_ERROR(err, CJOSE_ERR_NO_MEMORY); return false; } if (random) { if (RAND_bytes((unsigned char *)*buffer, bytes) != 1) { cjose_get_dealloc()(*buffer); CJOSE_ERROR(err, CJOSE_ERR_CRYPTO); return false; } } else { memset(*buffer, 0, bytes); } return true; }
static bool _cjose_jws_build_cser(cjose_jws_t *jws, cjose_err *err) { // both sign and import should be setting these - but check just in case if (NULL == jws->hdr_b64u || NULL == jws->dat_b64u || NULL == jws->sig_b64u) { return false; } // compute length of compact serialization jws->cser_len = jws->hdr_b64u_len + jws->dat_b64u_len + jws->sig_b64u_len + 3; // allocate buffer for compact serialization assert(NULL == jws->cser); jws->cser = (char *)cjose_get_alloc()(jws->cser_len); if (NULL == jws->cser) { CJOSE_ERROR(err, CJOSE_ERR_NO_MEMORY); return false; } // build the compact serialization snprintf(jws->cser, jws->cser_len, "%s.%s.%s", jws->hdr_b64u, jws->dat_b64u, jws->sig_b64u); 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 _EC_private_fields( const cjose_jwk_t *jwk, json_t *json, cjose_err *err) { ec_keydata *keydata = (ec_keydata *)jwk->keydata; const BIGNUM *bnD = EC_KEY_get0_private_key(keydata->key); uint8_t *buffer = NULL; char *b64u = NULL; size_t len = 0, offset = 0; json_t *field = NULL; bool result = false; // track expected binary data size uint8_t numsize = _ec_size_for_curve(keydata->crv, err); // short circuit if 'd' is NULL or 0 if (!bnD || BN_is_zero(bnD)) { return true; } buffer = cjose_get_alloc()(numsize); if (!buffer) { CJOSE_ERROR(err, CJOSE_ERR_NO_MEMORY); goto _ec_to_string_cleanup; } offset = numsize - BN_num_bytes(bnD); memset(buffer, 0, numsize); BN_bn2bin(bnD, (buffer + offset)); if (!cjose_base64url_encode(buffer, numsize, &b64u, &len, err)) { goto _ec_to_string_cleanup; } field = json_stringn(b64u, len); if (!field) { CJOSE_ERROR(err, CJOSE_ERR_NO_MEMORY); goto _ec_to_string_cleanup; } json_object_set(json, "d", field); json_decref(field); field = NULL; cjose_get_dealloc()(b64u); b64u = NULL; result = true; _ec_to_string_cleanup: if (buffer) { cjose_get_dealloc()(buffer); } return result; }
static inline bool _RSA_json_field( BIGNUM *param, const char *name, json_t *json, cjose_err *err) { json_t *field = NULL; uint8_t *data = NULL; char *b64u = NULL; size_t datalen = 0, b64ulen = 0; bool result = false; if (!param) { return true; } datalen = BN_num_bytes(param); data = cjose_get_alloc()(sizeof(uint8_t) * datalen); if (!data) { CJOSE_ERROR(err, CJOSE_ERR_NO_MEMORY); goto RSA_json_field_cleanup; } BN_bn2bin(param, data); if (!cjose_base64url_encode(data, datalen, &b64u, &b64ulen, err)) { goto RSA_json_field_cleanup; } field = json_stringn(b64u, b64ulen); if (!field) { CJOSE_ERROR(err, CJOSE_ERR_NO_MEMORY); goto RSA_json_field_cleanup; } json_object_set(json, name, field); json_decref(field); field = NULL; result = true; RSA_json_field_cleanup: if (b64u) { cjose_get_dealloc()(b64u); b64u = NULL; } if (data) { cjose_get_dealloc()(data); data = NULL; } return result; }
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) { CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG); return false; } strncpy(*dst, src, len); (*dst)[len] = 0; return true; }
static cjose_jwk_t *_EC_new(cjose_jwk_ec_curve crv, EC_KEY *ec, cjose_err *err) { ec_keydata *keydata = cjose_get_alloc()(sizeof(ec_keydata)); if (!keydata) { CJOSE_ERROR(err, CJOSE_ERR_NO_MEMORY); return NULL; } keydata->crv = crv; keydata->key = ec; cjose_jwk_t *jwk = cjose_get_alloc()(sizeof(cjose_jwk_t)); if (!jwk) { CJOSE_ERROR(err, CJOSE_ERR_NO_MEMORY); cjose_get_dealloc()(keydata); return NULL; } memset(jwk, 0, sizeof(cjose_jwk_t)); jwk->retained = 1; jwk->kty = CJOSE_JWK_KTY_EC; switch (crv) { case CJOSE_JWK_EC_P_256: jwk->keysize = 256; break; case CJOSE_JWK_EC_P_384: jwk->keysize = 384; break; case CJOSE_JWK_EC_P_521: jwk->keysize = 521; break; } jwk->keydata = keydata; jwk->fns = &EC_FNTABLE; return jwk; }
static inline cjose_jwk_t *_RSA_new(RSA *rsa, cjose_err *err) { cjose_jwk_t *jwk = cjose_get_alloc()(sizeof(cjose_jwk_t)); if (!jwk) { CJOSE_ERROR(err, CJOSE_ERR_NO_MEMORY); return NULL; } memset(jwk, 0, sizeof(cjose_jwk_t)); jwk->retained = 1; jwk->kty = CJOSE_JWK_KTY_RSA; jwk->keysize = RSA_size(rsa) * 8; jwk->keydata = rsa; jwk->fns = &RSA_FNTABLE; return jwk; }
static cjose_jwk_t *_oct_new(uint8_t *buffer, size_t keysize, cjose_err *err) { cjose_jwk_t *jwk = (cjose_jwk_t *)cjose_get_alloc()(sizeof(cjose_jwk_t)); if (NULL == jwk) { CJOSE_ERROR(err, CJOSE_ERR_NO_MEMORY); } else { memset(jwk, 0, sizeof(cjose_jwk_t)); jwk->retained = 1; jwk->kty = CJOSE_JWK_KTY_OCT; jwk->keysize = keysize; jwk->keydata = buffer; jwk->fns = &OCT_FNTABLE; } return jwk; }
static bool _cjose_jws_build_dat(cjose_jws_t *jws, const uint8_t *plaintext, size_t plaintext_len, cjose_err *err) { // copy plaintext data jws->dat_len = plaintext_len; jws->dat = (uint8_t *)cjose_get_alloc()(jws->dat_len); if (NULL == jws->dat) { CJOSE_ERROR(err, CJOSE_ERR_NO_MEMORY); return false; } memcpy(jws->dat, plaintext, jws->dat_len); // base64url encode data if (!cjose_base64url_encode((const uint8_t *)plaintext, plaintext_len, &jws->dat_b64u, &jws->dat_b64u_len, err)) { return false; } return true; }
cjose_jwk_t *cjose_jwk_create_oct_random(size_t keysize, cjose_err *err) { cjose_jwk_t * jwk = NULL; uint8_t * buffer = NULL; if (0 == keysize) { CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG); goto create_oct_failed; } // resize to bytes size_t buffersize = sizeof(uint8_t) * (keysize / 8); buffer = (uint8_t *)cjose_get_alloc()(buffersize); if (NULL == buffer) { CJOSE_ERROR(err, CJOSE_ERR_NO_MEMORY); goto create_oct_failed; } if (1 != RAND_bytes(buffer, buffersize)) { goto create_oct_failed; } jwk = _oct_new(buffer, keysize, err); if (NULL == jwk) { goto create_oct_failed; } return jwk; create_oct_failed: if (buffer) { cjose_get_dealloc()(buffer); buffer = NULL; } return NULL; }
cjose_jwk_t * cjose_jwk_create_oct_spec( const uint8_t *data, size_t len, cjose_err *err) { cjose_jwk_t * jwk = NULL; uint8_t * buffer = NULL; if (NULL == data || 0 == len) { CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG); goto create_oct_failed; } buffer = (uint8_t *)cjose_get_alloc()(len); if (!buffer) { CJOSE_ERROR(err, CJOSE_ERR_NO_MEMORY); goto create_oct_failed; } memcpy(buffer, data, len); jwk = _oct_new(buffer, len * 8, err); if (NULL == jwk) { goto create_oct_failed; } return jwk; create_oct_failed: if (buffer) { cjose_get_dealloc()(buffer); buffer = NULL; } return NULL; }
bool cjose_jwk_set_kid( cjose_jwk_t *jwk, const char *kid, size_t len, cjose_err *err) { if (!jwk || !kid) { CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG); return false; } if (jwk->kid) { cjose_get_dealloc()(jwk->kid); } jwk->kid = (char *)cjose_get_alloc()(len+1); if (!jwk->kid) { CJOSE_ERROR(err, CJOSE_ERR_NO_MEMORY); return false; } strncpy(jwk->kid, kid, len+1); return true; }
static bool _cjose_jws_build_sig_rs(cjose_jws_t *jws, const cjose_jwk_t *jwk, cjose_err *err) { // ensure jwk is private RSA if (jwk->kty != CJOSE_JWK_KTY_RSA) { CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG); return false; } RSA *rsa = (RSA *)jwk->keydata; BIGNUM *rsa_n = NULL, *rsa_e = NULL, *rsa_d = NULL; _cjose_jwk_rsa_get(rsa, &rsa_n, &rsa_e, &rsa_d); if (!rsa || !rsa_e || !rsa_n || !rsa_d) { CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG); return false; } // allocate buffer for signature jws->sig_len = RSA_size((RSA *)jwk->keydata); jws->sig = (uint8_t *)cjose_get_alloc()(jws->sig_len); if (NULL == jws->sig) { CJOSE_ERROR(err, CJOSE_ERR_NO_MEMORY); 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) { CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG); return false; } const char *alg = json_string_value(alg_obj); // build digest using SHA-256/384/512 digest algorithm int digest_alg = -1; if (strcmp(alg, CJOSE_HDR_ALG_RS256) == 0) digest_alg = NID_sha256; else if (strcmp(alg, CJOSE_HDR_ALG_RS384) == 0) digest_alg = NID_sha384; else if (strcmp(alg, CJOSE_HDR_ALG_RS512) == 0) digest_alg = NID_sha512; if (-1 == digest_alg) { CJOSE_ERROR(err, CJOSE_ERR_CRYPTO); return false; } unsigned int siglen; if (RSA_sign(digest_alg, jws->dig, jws->dig_len, jws->sig, &siglen, (RSA *)jwk->keydata) != 1) { CJOSE_ERROR(err, CJOSE_ERR_CRYPTO); return false; } jws->sig_len = siglen; // 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_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-256/384/512 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 = EVP_MD_size(digest_alg); 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 #if defined(CJOSE_OPENSSL_11X) ctx = HMAC_CTX_new(); #else ctx = cjose_get_alloc()(sizeof(HMAC_CTX)); #endif if (NULL == ctx) { CJOSE_ERROR(err, CJOSE_ERR_NO_MEMORY); goto _cjose_jws_build_dig_hmac_sha_cleanup; } #if !defined(CJOSE_OPENSSL_11X) HMAC_CTX_init(ctx); #endif // 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) { #if defined(CJOSE_OPENSSL_11X) HMAC_CTX_free(ctx); #else HMAC_CTX_cleanup(ctx); cjose_get_dealloc()(ctx); #endif } return retval; }
cjose_jws_t *cjose_jws_sign( const cjose_jwk_t *jwk, 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 == protected_header || NULL == plaintext) { CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG); return NULL; } // allocate and initialize JWS jws = (cjose_jws_t *)cjose_get_alloc()(sizeof(cjose_jws_t)); if (NULL == jws) { CJOSE_ERROR(err, CJOSE_ERR_NO_MEMORY); return NULL; } memset(jws, 0, sizeof(cjose_jws_t)); // build JWS header if (!_cjose_jws_build_hdr(jws, protected_header, err)) { cjose_jws_release(jws); return NULL; } // validate JWS header if (!_cjose_jws_validate_hdr(jws, err)) { cjose_jws_release(jws); return NULL; } // build the JWS data segment if (!_cjose_jws_build_dat(jws, plaintext, plaintext_len, err)) { cjose_jws_release(jws); return NULL; } // build JWS digest (hashed signing input value) if (!jws->fns.digest(jws, jwk, err)) { cjose_jws_release(jws); return NULL; } // sign the JWS digest if (!jws->fns.sign(jws, jwk, err)) { cjose_jws_release(jws); return NULL; } // build JWS compact serialization if (!_cjose_jws_build_cser(jws, err)) { cjose_jws_release(jws); return NULL; } return jws; }
static bool _cjose_jws_build_sig_ec(cjose_jws_t *jws, const cjose_jwk_t *jwk, cjose_err *err) { bool retval = false; // ensure jwk is EC if (jwk->kty != CJOSE_JWK_KTY_EC) { CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG); return false; } ec_keydata *keydata = (ec_keydata *)jwk->keydata; EC_KEY *ec = keydata->key; ECDSA_SIG *ecdsa_sig = ECDSA_do_sign(jws->dig, jws->dig_len, ec); if (NULL == ecdsa_sig) { CJOSE_ERROR(err, CJOSE_ERR_CRYPTO); goto _cjose_jws_build_sig_ec_cleanup; } // allocate buffer for signature switch (keydata->crv) { case CJOSE_JWK_EC_P_256: jws->sig_len = 32 * 2; break; case CJOSE_JWK_EC_P_384: jws->sig_len = 48 * 2; break; case CJOSE_JWK_EC_P_521: jws->sig_len = 66 * 2; break; case CJOSE_JWK_EC_INVALID: jws->sig_len = 0; break; } jws->sig = (uint8_t *)cjose_get_alloc()(jws->sig_len); if (NULL == jws->sig) { CJOSE_ERROR(err, CJOSE_ERR_NO_MEMORY); goto _cjose_jws_build_sig_ec_cleanup; } memset(jws->sig, 0, jws->sig_len); const BIGNUM *pr, *ps; #if defined(CJOSE_OPENSSL_11X) ECDSA_SIG_get0(ecdsa_sig, &pr, &ps); #else pr = ecdsa_sig->r; ps = ecdsa_sig->s; #endif int rlen = BN_num_bytes(pr); int slen = BN_num_bytes(ps); BN_bn2bin(pr, jws->sig + jws->sig_len / 2 - rlen); BN_bn2bin(ps, jws->sig + jws->sig_len - slen); // 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); goto _cjose_jws_build_sig_ec_cleanup; } retval = true; _cjose_jws_build_sig_ec_cleanup: if (ecdsa_sig) ECDSA_SIG_free(ecdsa_sig); return retval; }
static bool _EC_public_fields( const cjose_jwk_t *jwk, json_t *json, cjose_err *err) { ec_keydata *keydata = (ec_keydata *)jwk->keydata; const EC_GROUP *params = NULL; const EC_POINT *pub = NULL; BIGNUM *bnX = NULL, *bnY = NULL; uint8_t *buffer = NULL; char *b64u = NULL; size_t len = 0, offset = 0; json_t *field = NULL; bool result = false; // track expected binary data size uint8_t numsize = _ec_size_for_curve(keydata->crv, err); // output the curve field = json_string(_ec_name_for_curve(keydata->crv, err)); if (!field) { CJOSE_ERROR(err, CJOSE_ERR_NO_MEMORY); goto _ec_to_string_cleanup; } json_object_set(json, "crv", field); json_decref(field); field = NULL; // obtain the public key pub = EC_KEY_get0_public_key(keydata->key); params = EC_KEY_get0_group(keydata->key); if (!pub || !params) { CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG); goto _ec_to_string_cleanup; } buffer = cjose_get_alloc()(numsize); bnX = BN_new(); bnY = BN_new(); if (!buffer || !bnX || !bnY) { CJOSE_ERROR(err, CJOSE_ERR_NO_MEMORY); goto _ec_to_string_cleanup; } if (1 != EC_POINT_get_affine_coordinates_GFp(params, pub, bnX, bnY, NULL)) { CJOSE_ERROR(err, CJOSE_ERR_NO_MEMORY); goto _ec_to_string_cleanup; } // output the x coordinate offset = numsize - BN_num_bytes(bnX); memset(buffer, 0, numsize); BN_bn2bin(bnX, (buffer + offset)); if (!cjose_base64url_encode(buffer, numsize, &b64u, &len, err)) { goto _ec_to_string_cleanup; } field = json_stringn(b64u, len); if (!field) { CJOSE_ERROR(err, CJOSE_ERR_NO_MEMORY); goto _ec_to_string_cleanup; } json_object_set(json, "x", field); json_decref(field); field = NULL; cjose_get_dealloc()(b64u); b64u = NULL; // output the y coordinate offset = numsize - BN_num_bytes(bnY); memset(buffer, 0, numsize); BN_bn2bin(bnY, (buffer + offset)); if (!cjose_base64url_encode(buffer, numsize, &b64u, &len, err)) { goto _ec_to_string_cleanup; } field = json_stringn(b64u, len); if (!field) { CJOSE_ERROR(err, CJOSE_ERR_NO_MEMORY); goto _ec_to_string_cleanup; } json_object_set(json, "y", field); json_decref(field); field = NULL; cjose_get_dealloc()(b64u); b64u = NULL; result = true; _ec_to_string_cleanup: if (field) { json_decref(field); } if (bnX) { BN_free(bnX); } if (bnY) { BN_free(bnY); } if (buffer) { cjose_get_dealloc()(buffer); } if (b64u) { cjose_get_dealloc()(b64u); } return result; }
cjose_jws_t *cjose_jws_import(const char *cser, size_t cser_len, cjose_err *err) { cjose_jws_t *jws = NULL; size_t len = 0; if (NULL == cser) { CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG); return NULL; } // allocate and initialize a new JWS object jws = (cjose_jws_t *)cjose_get_alloc()(sizeof(cjose_jws_t)); if (NULL == jws) { CJOSE_ERROR(err, CJOSE_ERR_NO_MEMORY); return NULL; } memset(jws, 0, sizeof(cjose_jws_t)); // find the indexes of the dots int idx = 0; int d[2] = { 0, 0 }; for (int i = 0; i < cser_len && idx < 2; ++i) { if (cser[i] == '.') { d[idx++] = i; } } // fail if we didn't find both dots if (0 == d[1]) { CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG); cjose_jws_release(jws); return NULL; } // 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_base64url_decode(jws->hdr_b64u, jws->hdr_b64u_len, &hdr_str, &len, err) || NULL == hdr_str) { cjose_jws_release(jws); return NULL; } // deserialize JSON header jws->hdr = json_loadb((const char *)hdr_str, len, 0, NULL); cjose_get_dealloc()(hdr_str); if (NULL == jws->hdr) { CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG); cjose_jws_release(jws); return NULL; } // validate the JSON header segment if (!_cjose_jws_validate_hdr(jws, err)) { // make an exception for alg=none so that it will import/parse but not sign/verify json_t *alg_obj = json_object_get(jws->hdr, CJOSE_HDR_ALG); if (NULL == alg_obj) { CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG); return NULL; } const char *alg = json_string_value(alg_obj); if ((!alg) || (strcmp(alg, CJOSE_HDR_ALG_NONE) != 0)) { CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG); cjose_jws_release(jws); return NULL; } } // 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_base64url_decode(jws->dat_b64u, jws->dat_b64u_len, &jws->dat, &jws->dat_len, err)) { cjose_jws_release(jws); return NULL; } // 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_base64url_decode(jws->sig_b64u, jws->sig_b64u_len, &jws->sig, &jws->sig_len, err)) { cjose_jws_release(jws); return NULL; } return jws; }
static bool _cjose_jws_build_dig_sha(cjose_jws_t *jws, const cjose_jwk_t *jwk, cjose_err *err) { bool retval = false; EVP_MD_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-256/384/512 digest algorithm const EVP_MD *digest_alg = NULL; if ((strcmp(alg, CJOSE_HDR_ALG_RS256) == 0) || (strcmp(alg, CJOSE_HDR_ALG_PS256) == 0) || (strcmp(alg, CJOSE_HDR_ALG_ES256) == 0)) digest_alg = EVP_sha256(); else if ((strcmp(alg, CJOSE_HDR_ALG_RS384) == 0) || (strcmp(alg, CJOSE_HDR_ALG_PS384) == 0) || (strcmp(alg, CJOSE_HDR_ALG_ES384) == 0)) digest_alg = EVP_sha384(); else if ((strcmp(alg, CJOSE_HDR_ALG_RS512) == 0) || (strcmp(alg, CJOSE_HDR_ALG_PS512) == 0) || (strcmp(alg, CJOSE_HDR_ALG_ES512) == 0)) digest_alg = EVP_sha512(); if (NULL == digest_alg) { CJOSE_ERROR(err, CJOSE_ERR_CRYPTO); goto _cjose_jws_build_dig_sha_cleanup; } if (NULL != jws->dig) { cjose_get_dealloc()(jws->dig); jws->dig = NULL; } // allocate buffer for digest jws->dig_len = EVP_MD_size(digest_alg); 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_sha_cleanup; } // instantiate and initialize a new mac digest context ctx = EVP_MD_CTX_create(); if (NULL == ctx) { CJOSE_ERROR(err, CJOSE_ERR_CRYPTO); goto _cjose_jws_build_dig_sha_cleanup; } EVP_MD_CTX_init(ctx); // create digest as DIGEST(B64U(HEADER).B64U(DATA)) if (EVP_DigestInit_ex(ctx, digest_alg, NULL) != 1) { CJOSE_ERROR(err, CJOSE_ERR_CRYPTO); goto _cjose_jws_build_dig_sha_cleanup; } if (EVP_DigestUpdate(ctx, jws->hdr_b64u, jws->hdr_b64u_len) != 1) { CJOSE_ERROR(err, CJOSE_ERR_CRYPTO); goto _cjose_jws_build_dig_sha_cleanup; } if (EVP_DigestUpdate(ctx, ".", 1) != 1) { CJOSE_ERROR(err, CJOSE_ERR_CRYPTO); goto _cjose_jws_build_dig_sha_cleanup; } if (EVP_DigestUpdate(ctx, jws->dat_b64u, jws->dat_b64u_len) != 1) { CJOSE_ERROR(err, CJOSE_ERR_CRYPTO); goto _cjose_jws_build_dig_sha_cleanup; } if (EVP_DigestFinal_ex(ctx, jws->dig, NULL) != 1) { CJOSE_ERROR(err, CJOSE_ERR_CRYPTO); goto _cjose_jws_build_dig_sha_cleanup; } // if we got this far - success retval = true; _cjose_jws_build_dig_sha_cleanup: if (NULL != ctx) { EVP_MD_CTX_destroy(ctx); } return retval; }
static bool _cjose_jws_build_sig_ps(cjose_jws_t *jws, const cjose_jwk_t *jwk, cjose_err *err) { bool retval = false; uint8_t *em = NULL; size_t em_len = 0; // ensure jwk is private RSA if (jwk->kty != CJOSE_JWK_KTY_RSA) { CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG); goto _cjose_jws_build_sig_ps_cleanup; } RSA *rsa = (RSA *)jwk->keydata; BIGNUM *rsa_n = NULL, *rsa_e = NULL, *rsa_d = NULL; _cjose_jwk_rsa_get(rsa, &rsa_n, &rsa_e, &rsa_d); if (!rsa || !rsa_e || !rsa_n || !rsa_d) { CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG); 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) { CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG); return false; } const char *alg = json_string_value(alg_obj); // build digest using SHA-256/384/512 digest algorithm const EVP_MD *digest_alg = NULL; if (strcmp(alg, CJOSE_HDR_ALG_PS256) == 0) digest_alg = EVP_sha256(); else if (strcmp(alg, CJOSE_HDR_ALG_PS384) == 0) digest_alg = EVP_sha384(); else if (strcmp(alg, CJOSE_HDR_ALG_PS512) == 0) digest_alg = EVP_sha512(); if (NULL == digest_alg) { CJOSE_ERROR(err, CJOSE_ERR_CRYPTO); goto _cjose_jws_build_sig_ps_cleanup; } // apply EMSA-PSS encoding (RFC-3447, 8.1.1, step 1) // (RSA_padding_add_PKCS1_PSS includes PKCS1_MGF1, -1 => saltlen = hashlen) em_len = RSA_size((RSA *)jwk->keydata); em = (uint8_t *)cjose_get_alloc()(em_len); if (NULL == em) { CJOSE_ERROR(err, CJOSE_ERR_NO_MEMORY); goto _cjose_jws_build_sig_ps_cleanup; } if (RSA_padding_add_PKCS1_PSS((RSA *)jwk->keydata, em, jws->dig, digest_alg, -1) != 1) { CJOSE_ERROR(err, CJOSE_ERR_CRYPTO); goto _cjose_jws_build_sig_ps_cleanup; } // sign the digest (RFC-3447, 8.1.1, step 2) jws->sig_len = em_len; jws->sig = (uint8_t *)cjose_get_alloc()(jws->sig_len); if (NULL == jws->sig) { CJOSE_ERROR(err, CJOSE_ERR_NO_MEMORY); goto _cjose_jws_build_sig_ps_cleanup; } if (RSA_private_encrypt(em_len, em, jws->sig, (RSA *)jwk->keydata, RSA_NO_PADDING) != jws->sig_len) { CJOSE_ERROR(err, CJOSE_ERR_CRYPTO); goto _cjose_jws_build_sig_ps_cleanup; } // base64url encode signed digest if (!cjose_base64url_encode((const uint8_t *)jws->sig, jws->sig_len, &jws->sig_b64u, &jws->sig_b64u_len, err)) { goto _cjose_jws_build_sig_ps_cleanup; } // if we got this far - success retval = true; _cjose_jws_build_sig_ps_cleanup: cjose_get_dealloc()(em); return retval; }
cjose_jwk_t *cjose_jwk_derive_ecdh_ephemeral_key( cjose_jwk_t *jwk_self, cjose_jwk_t *jwk_peer, cjose_err *err) { EVP_PKEY_CTX *ctx = NULL; EVP_PKEY *pkey_self = NULL; EVP_PKEY *pkey_peer = NULL; uint8_t *secret = NULL; size_t secret_len = 0; uint8_t *ephemeral_key = NULL; size_t ephemeral_key_len = 0; cjose_jwk_t *jwk_ephemeral_key = NULL; // get EVP_KEY from jwk_self if (!_cjose_jwk_evp_key_from_ec_key(jwk_self, &pkey_self, err)) { goto _cjose_jwk_derive_shared_secret_fail; } // get EVP_KEY from jwk_peer if (!_cjose_jwk_evp_key_from_ec_key(jwk_peer, &pkey_peer, err)) { goto _cjose_jwk_derive_shared_secret_fail; } // create derivation context based on local key pair ctx = EVP_PKEY_CTX_new(pkey_self, NULL); if (NULL == ctx) { CJOSE_ERROR(err, CJOSE_ERR_CRYPTO); goto _cjose_jwk_derive_shared_secret_fail; } // initialize derivation context if (1 != EVP_PKEY_derive_init(ctx)) { CJOSE_ERROR(err, CJOSE_ERR_CRYPTO); goto _cjose_jwk_derive_shared_secret_fail; } // provide the peer public key if (1 != EVP_PKEY_derive_set_peer(ctx, pkey_peer)) { CJOSE_ERROR(err, CJOSE_ERR_CRYPTO); goto _cjose_jwk_derive_shared_secret_fail; } // determine buffer length for shared secret if(1 != EVP_PKEY_derive(ctx, NULL, &secret_len)) { CJOSE_ERROR(err, CJOSE_ERR_CRYPTO); goto _cjose_jwk_derive_shared_secret_fail; } // allocate buffer for shared secret secret = (uint8_t *)cjose_get_alloc()(secret_len); if (NULL == secret) { CJOSE_ERROR(err, CJOSE_ERR_NO_MEMORY); goto _cjose_jwk_derive_shared_secret_fail; } memset(secret, 0, secret_len); // derive the shared secret if (1 != (EVP_PKEY_derive(ctx, secret, &secret_len))) { CJOSE_ERROR(err, CJOSE_ERR_NO_MEMORY); goto _cjose_jwk_derive_shared_secret_fail; } // HKDF of the DH shared secret (SHA256, no salt, no info, 256 bit expand) ephemeral_key_len = 32; ephemeral_key = (uint8_t *)cjose_get_alloc()(ephemeral_key_len); if (!cjose_jwk_hkdf(EVP_sha256(), (uint8_t *)"", 0, (uint8_t *)"", 0, secret, secret_len, ephemeral_key, ephemeral_key_len, err)) { goto _cjose_jwk_derive_shared_secret_fail; } // create a JWK of the shared secret jwk_ephemeral_key = cjose_jwk_create_oct_spec( ephemeral_key, ephemeral_key_len, err); if (NULL == jwk_ephemeral_key) { goto _cjose_jwk_derive_shared_secret_fail; } // happy path EVP_PKEY_CTX_free(ctx); EVP_PKEY_free(pkey_self); EVP_PKEY_free(pkey_peer); cjose_get_dealloc()(secret); cjose_get_dealloc()(ephemeral_key); return jwk_ephemeral_key; // fail path _cjose_jwk_derive_shared_secret_fail: if (NULL != ctx) { EVP_PKEY_CTX_free(ctx); } if (NULL != pkey_self) { EVP_PKEY_free(pkey_self); } if (NULL != pkey_peer) { EVP_PKEY_free(pkey_peer); } if (NULL != jwk_ephemeral_key) { cjose_jwk_release(jwk_ephemeral_key); } cjose_get_dealloc()(secret); cjose_get_dealloc()(ephemeral_key); return NULL; }