static bool _cjose_jwe_build_hdr( cjose_jwe_t *jwe, 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) { CJOSE_ERROR(err, CJOSE_ERR_NO_MEMORY); return false; } // copy the serialized header to JWE (hdr_str is owned by header object) jwe->part[0].raw = (uint8_t *)strdup(hdr_str); if (NULL == jwe->part[0].raw) { CJOSE_ERROR(err, CJOSE_ERR_NO_MEMORY); cjose_get_dealloc()(hdr_str); return false; } jwe->part[0].raw_len = strlen(hdr_str); cjose_get_dealloc()(hdr_str); 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 void _oct_free(cjose_jwk_t *jwk) { uint8_t * buffer = (uint8_t *)jwk->keydata; jwk->keydata = NULL; if (buffer) { cjose_get_dealloc()(buffer); } cjose_get_dealloc()(jwk); }
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; }
/* * perform compact serialization on a JWT and return the resulting string */ char *oidc_jwt_serialize(apr_pool_t *pool, oidc_jwt_t *jwt, oidc_jose_error_t *err) { cjose_err cjose_err; const char *cser = NULL; if (strcmp(jwt->header.alg, "none") != 0) { if (cjose_jws_export(jwt->cjose_jws, &cser, &cjose_err) == FALSE) { oidc_jose_error(err, "cjose_jws_export failed: %s", oidc_cjose_e2s(pool, cjose_err)); return NULL; } } else { char *s_payload = json_dumps(jwt->payload.value.json, JSON_PRESERVE_ORDER | JSON_COMPACT); char *out = NULL; size_t out_len; if (cjose_base64url_encode((const uint8_t *)s_payload, strlen(s_payload), &out, &out_len, &cjose_err) == FALSE) return FALSE; cser = apr_pstrndup(pool, out, out_len); cjose_get_dealloc()(out); free(s_payload); cser = apr_psprintf(pool, "eyJhbGciOiJub25lIn0.%s.", cser); } return apr_pstrdup(pool, cser); }
static bool _oct_private_fields( const cjose_jwk_t *jwk, json_t *json, cjose_err *err) { json_t *field = NULL; char *k = NULL; size_t klen = 0; uint8_t *keydata = (uint8_t *)jwk->keydata; size_t keysize = jwk->keysize / 8; if (!cjose_base64url_encode(keydata, keysize, &k, &klen, err)) { return false; } field = json_stringn(k, klen); cjose_get_dealloc()(k); k = NULL; if (!field) { return false; } json_object_set(json, "k", field); json_decref(field); return true; }
static cjose_jwk_t *_cjose_jwk_import_oct(json_t *jwk_json, cjose_err *err) { cjose_jwk_t *jwk = NULL; uint8_t *k_buffer = NULL; // get the decoded value of k (buflen = 0 means no particular expected len) size_t k_buflen = 0; if (!_decode_json_object_base64url_attribute( jwk_json, CJOSE_JWK_K_STR, &k_buffer, &k_buflen, err)) { CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG); goto import_oct_cleanup; } // create the jwk jwk = cjose_jwk_create_oct_spec(k_buffer, k_buflen, err); import_oct_cleanup: if (NULL != k_buffer) { cjose_get_dealloc()(k_buffer); } return jwk; }
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; }
/* * encrypt JWT */ apr_byte_t oidc_jwt_encrypt(apr_pool_t *pool, oidc_jwt_t *jwe, oidc_jwk_t *jwk, const char *payload, char **serialized, oidc_jose_error_t *err) { cjose_header_t *hdr = (cjose_header_t *)jwe->header.value.json; if (jwe->header.alg) oidc_jwt_hdr_set(jwe, CJOSE_HDR_ALG, jwe->header.alg); if (jwe->header.kid) oidc_jwt_hdr_set(jwe, CJOSE_HDR_KID, jwe->header.kid); if (jwe->header.enc) oidc_jwt_hdr_set(jwe, CJOSE_HDR_ENC, jwe->header.enc); cjose_err cjose_err; cjose_jwe_t *cjose_jwe = cjose_jwe_encrypt(jwk->cjose_jwk, hdr, (const uint8_t *) payload, strlen(payload), &cjose_err); if (cjose_jwe == NULL) { oidc_jose_error(err, "cjose_jwe_encrypt failed: %s", oidc_cjose_e2s(pool, cjose_err)); return FALSE; } char *cser = cjose_jwe_export(cjose_jwe, &cjose_err); if (cser == NULL) { oidc_jose_error(err, "cjose_jwe_export failed: %s", oidc_cjose_e2s(pool, cjose_err)); return FALSE; } *serialized = apr_pstrdup(pool, cser); cjose_get_dealloc()(cser); cjose_jwe_release(cjose_jwe); return TRUE; }
static void _EC_free(cjose_jwk_t *jwk) { ec_keydata *keydata = (ec_keydata *)jwk->keydata; jwk->keydata = NULL; if (keydata) { EC_KEY *ec = keydata->key; keydata->key = NULL; if (ec) { EC_KEY_free(ec); } cjose_get_dealloc()(keydata); } cjose_get_dealloc()(jwk); }
static void _RSA_free(cjose_jwk_t *jwk) { RSA *rsa = (RSA *)jwk->keydata; jwk->keydata = NULL; if (rsa) { RSA_free(rsa); } cjose_get_dealloc()(jwk); }
static bool _cjose_jwe_set_cek_a256gcm( cjose_jwe_t *jwe, const cjose_jwk_t *jwk, cjose_err *err) { // 256 bits = 32 bytes static const size_t keysize = 32; // if no JWK is provided, generate a random key if (NULL == jwk) { cjose_get_dealloc()(jwe->cek); if (!_cjose_jwe_malloc(keysize, true, &jwe->cek, err)) { return false; } jwe->cek_len = keysize; } else { // if a JWK is provided, it must be a symmetric key of correct size if (CJOSE_JWK_KTY_OCT != cjose_jwk_get_kty(jwk, err) || jwk->keysize != keysize*8 || NULL == jwk->keydata) { CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG); return false; } // copy the key material directly from jwk to the jwe->cek cjose_get_dealloc()(jwe->cek); if (!_cjose_jwe_malloc(keysize, false, &jwe->cek, err)) { return false; } memcpy(jwe->cek, jwk->keydata, keysize); jwe->cek_len = keysize; } return true; }
void cjose_jwe_release( cjose_jwe_t *jwe) { if (NULL == jwe) { return; } if (NULL != jwe->hdr) { json_decref(jwe->hdr); } for (int i = 0; i < 5; ++i) { cjose_get_dealloc()(jwe->part[i].raw); cjose_get_dealloc()(jwe->part[i].b64u); } cjose_get_dealloc()(jwe->cek); cjose_get_dealloc()(jwe->dat); cjose_get_dealloc()(jwe); }
static bool _cjose_jwe_set_iv_a256gcm( cjose_jwe_t *jwe, cjose_err *err) { // generate IV as random 96 bit value cjose_get_dealloc()(jwe->part[2].raw); jwe->part[2].raw_len = 12; if (!_cjose_jwe_malloc(jwe->part[2].raw_len, true, &jwe->part[2].raw, err)) { return false; } return true; }
static bool _cjose_jwe_encrypt_ek_rsa_oaep( cjose_jwe_t *jwe, const cjose_jwk_t *jwk, cjose_err *err) { // jwk must be RSA and have the necessary public parts set if (jwk->kty != CJOSE_JWK_KTY_RSA || NULL == jwk->keydata || NULL == ((RSA *)jwk->keydata)->e || NULL == ((RSA *)jwk->keydata)->n) { CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG); return false; } // generate random cek if (!jwe->fns.set_cek(jwe, NULL, err)) { return false; } // the size of the ek will match the size of the RSA key jwe->part[1].raw_len = RSA_size((RSA *)jwk->keydata); // for OAEP padding - the RSA size - 41 must be greater than input if (jwe->cek_len >= jwe->part[1].raw_len - 41) { CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG); return false; } // allocate memory for RSA encryption cjose_get_dealloc()(jwe->part[1].raw); if (!_cjose_jwe_malloc(jwe->part[1].raw_len, false, &jwe->part[1].raw, err)) { return false; } // encrypt the CEK using RSAES-OAEP if (RSA_public_encrypt(jwe->cek_len, jwe->cek, jwe->part[1].raw, (RSA *)jwk->keydata, RSA_PKCS1_OAEP_PADDING) != jwe->part[1].raw_len) { CJOSE_ERROR(err, CJOSE_ERR_CRYPTO); return false; } return true; }
void cjose_jws_release(cjose_jws_t *jws) { if (NULL == jws) { return; } if (NULL != jws->hdr) { json_decref(jws->hdr); } 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_get_dealloc()(jws->sig_b64u); cjose_get_dealloc()(jws->cser); cjose_get_dealloc()(jws); }
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; }
bool cjose_jwk_release(cjose_jwk_t *jwk) { if (!jwk) { return false; } --(jwk->retained); if (0 == jwk->retained) { cjose_get_dealloc()(jwk->kid); jwk->kid = NULL; // assumes freefunc is set assert(NULL != jwk->fns->free); jwk->fns->free(jwk); jwk = NULL; } return (NULL != jwk); }
/* * hash a sequence of bytes with a specific algorithm and return the result as a base64url-encoded \0 terminated string */ static apr_byte_t oidc_jose_hash_and_base64url_encode(apr_pool_t *pool, const char *openssl_hash_algo, const char *input, int input_len, char **output) { oidc_jose_error_t err; unsigned char *hashed = NULL; unsigned int hashed_len = 0; if (oidc_jose_hash_bytes(pool, openssl_hash_algo, (const unsigned char *) input, input_len, &hashed, &hashed_len, &err) == FALSE) { return FALSE; } char *out = NULL; size_t out_len; cjose_err cjose_err; if (cjose_base64url_encode(hashed, hashed_len, &out, &out_len, &cjose_err) == FALSE) return FALSE; *output = apr_pstrndup(pool, out, out_len); cjose_get_dealloc()(out); return TRUE; }
cjose_jwk_t *cjose_jwk_create_EC_random(cjose_jwk_ec_curve crv, cjose_err *err) { cjose_jwk_t * jwk = NULL; EC_KEY * ec = NULL; ec = EC_KEY_new_by_curve_name(crv); if (!ec) { CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG); goto create_EC_failed; } if (1 != EC_KEY_generate_key(ec)) { CJOSE_ERROR(err, CJOSE_ERR_NO_MEMORY); goto create_EC_failed; } jwk = _EC_new(crv, ec, err); if (!jwk) { goto create_EC_failed; } return jwk; create_EC_failed: if (jwk) { cjose_get_dealloc()(jwk); jwk = NULL; } if (ec) { EC_KEY_free(ec); ec = NULL; } return NULL; }
static bool _cjose_jwe_decrypt_ek_rsa_oaep( cjose_jwe_t *jwe, const cjose_jwk_t *jwk, cjose_err *err) { if (NULL == jwe || NULL == jwk) { CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG); return false; } // jwk must be RSA if (jwk->kty != CJOSE_JWK_KTY_RSA) { CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG); return false; } // we don't know the size of the key to expect, but must be < RSA_size cjose_get_dealloc()(jwe->cek); size_t buflen = RSA_size((RSA *)jwk->keydata); if (!_cjose_jwe_malloc(buflen, false, &jwe->cek, err)) { return false; } // decrypt the CEK using RSAES-OAEP jwe->cek_len = RSA_private_decrypt( jwe->part[1].raw_len, jwe->part[1].raw, jwe->cek, (RSA *)jwk->keydata, RSA_PKCS1_OAEP_PADDING); if (-1 == jwe->cek_len) { CJOSE_ERROR(err, CJOSE_ERR_CRYPTO); return false; } return true; }
/* * decrypt a JSON Web Token */ apr_byte_t oidc_jwe_decrypt(apr_pool_t *pool, const char *input_json, apr_hash_t *keys, char **s_json, oidc_jose_error_t *err, apr_byte_t import_must_succeed) { cjose_err cjose_err; cjose_jwe_t *jwe = cjose_jwe_import(input_json, strlen(input_json), &cjose_err); if (jwe != NULL) { size_t content_len = 0; uint8_t *decrypted = oidc_jwe_decrypt_impl(pool, jwe, keys, &content_len, err); if (decrypted != NULL) { decrypted[content_len] = '\0'; *s_json = apr_pstrdup(pool, (const char *) decrypted); cjose_get_dealloc()(decrypted); } cjose_jwe_release(jwe); } else if (import_must_succeed == FALSE) { *s_json = apr_pstrdup(pool, input_json); } else { oidc_jose_error(err, "cjose_jwe_import failed: %s", oidc_cjose_e2s(pool, cjose_err)); } return (*s_json != 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; }
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; }
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; }
char *cjose_jwk_to_json(const cjose_jwk_t *jwk, bool priv, cjose_err *err) { char *result = NULL; if (!jwk) { CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG); return NULL; } json_t *json = json_object(), *field = NULL; if (!json) { CJOSE_ERROR(err, CJOSE_ERR_NO_MEMORY); goto to_json_cleanup; } // set kty const char *kty = cjose_jwk_name_for_kty(jwk->kty, err); field = json_string(kty); if (!field) { CJOSE_ERROR(err, CJOSE_ERR_NO_MEMORY); goto to_json_cleanup; } json_object_set(json, "kty", field); json_decref(field); field = NULL; // set kid if (NULL != jwk->kid) { field = json_string(jwk->kid); if (!field) { CJOSE_ERROR(err, CJOSE_ERR_NO_MEMORY); goto to_json_cleanup; } json_object_set(json, CJOSE_JWK_KID_STR, field); json_decref(field); field = NULL; } // set public fields if (jwk->fns->public_json && !jwk->fns->public_json(jwk, json, err)) { goto to_json_cleanup; } // set private fields if (priv && jwk->fns->private_json && !jwk->fns->private_json(jwk, json, err)) { goto to_json_cleanup; } // generate the string ... char *str_jwk = json_dumps( json, JSON_ENCODE_ANY | JSON_COMPACT | JSON_PRESERVE_ORDER); if (!str_jwk) { CJOSE_ERROR(err, CJOSE_ERR_NO_MEMORY); goto to_json_cleanup; } result = strdup(str_jwk); cjose_get_dealloc()(str_jwk); to_json_cleanup: if (json) { json_decref(json); json = NULL; } if (field) { json_decref(field); field = NULL; } return result; }
static bool _cjose_jwe_decrypt_dat_a256gcm( cjose_jwe_t *jwe, cjose_err *err) { EVP_CIPHER_CTX *ctx = NULL; // get A256GCM cipher const EVP_CIPHER *cipher = EVP_aes_256_gcm(); if (NULL == cipher) { CJOSE_ERROR(err, CJOSE_ERR_CRYPTO); goto _cjose_jwe_decrypt_dat_a256gcm_fail; } // instantiate and initialize a new openssl cipher context ctx = EVP_CIPHER_CTX_new(); if (NULL == ctx) { CJOSE_ERROR(err, CJOSE_ERR_CRYPTO); goto _cjose_jwe_decrypt_dat_a256gcm_fail; } EVP_CIPHER_CTX_init(ctx); // initialize context for decryption using A256GCM cipher and CEK and IV if (EVP_DecryptInit_ex(ctx, cipher, NULL, jwe->cek, jwe->part[2].raw) != 1) { CJOSE_ERROR(err, CJOSE_ERR_CRYPTO); goto _cjose_jwe_decrypt_dat_a256gcm_fail; } // set the expected GCM-mode authentication tag if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, jwe->part[4].raw_len, jwe->part[4].raw) != 1) { CJOSE_ERROR(err, CJOSE_ERR_CRYPTO); goto _cjose_jwe_decrypt_dat_a256gcm_fail; } // set GCM mode AAD data (hdr_b64u) by setting "out" to NULL int bytes_decrypted = 0; if (EVP_DecryptUpdate(ctx, NULL, &bytes_decrypted, (unsigned char *)jwe->part[0].b64u, jwe->part[0].b64u_len) != 1 || bytes_decrypted != jwe->part[0].b64u_len) { CJOSE_ERROR(err, CJOSE_ERR_CRYPTO); goto _cjose_jwe_decrypt_dat_a256gcm_fail; } // allocate buffer for the plaintext cjose_get_dealloc()(jwe->dat); jwe->dat_len = jwe->part[3].raw_len; if (!_cjose_jwe_malloc(jwe->dat_len, false, &jwe->dat, err)) { goto _cjose_jwe_decrypt_dat_a256gcm_fail; } // decrypt ciphertext to plaintext buffer if (EVP_DecryptUpdate(ctx, jwe->dat, &bytes_decrypted, jwe->part[3].raw, jwe->part[3].raw_len) != 1) { CJOSE_ERROR(err, CJOSE_ERR_CRYPTO); goto _cjose_jwe_decrypt_dat_a256gcm_fail; } jwe->dat_len = bytes_decrypted; // finalize the encryption if (EVP_DecryptFinal_ex(ctx, NULL, &bytes_decrypted) != 1) { CJOSE_ERROR(err, CJOSE_ERR_CRYPTO); goto _cjose_jwe_decrypt_dat_a256gcm_fail; } EVP_CIPHER_CTX_free(ctx); return true; _cjose_jwe_decrypt_dat_a256gcm_fail: if (NULL != ctx) { EVP_CIPHER_CTX_free(ctx); } return false; }
static bool _cjose_jwe_encrypt_dat_a256gcm( cjose_jwe_t *jwe, const uint8_t *plaintext, size_t plaintext_len, cjose_err *err) { EVP_CIPHER_CTX *ctx = NULL; if (NULL == plaintext) { CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG); goto _cjose_jwe_encrypt_dat_fail; } // get A256GCM cipher const EVP_CIPHER *cipher = EVP_aes_256_gcm(); if (NULL == cipher) { CJOSE_ERROR(err, CJOSE_ERR_CRYPTO); goto _cjose_jwe_encrypt_dat_fail; } // instantiate and initialize a new openssl cipher context ctx = EVP_CIPHER_CTX_new(); if (NULL == ctx) { CJOSE_ERROR(err, CJOSE_ERR_CRYPTO); goto _cjose_jwe_encrypt_dat_fail; } EVP_CIPHER_CTX_init(ctx); // initialize context for encryption using A256GCM cipher and CEK and IV if (EVP_EncryptInit_ex(ctx, cipher, NULL, jwe->cek, jwe->part[2].raw) != 1) { CJOSE_ERROR(err, CJOSE_ERR_CRYPTO); goto _cjose_jwe_encrypt_dat_fail; } // we need the header in base64url encoding as input for encryption if ((NULL == jwe->part[0].b64u) && (!cjose_base64url_encode( (const uint8_t *)jwe->part[0].raw, jwe->part[0].raw_len, &jwe->part[0].b64u, &jwe->part[0].b64u_len, err))) { goto _cjose_jwe_encrypt_dat_fail; } // set GCM mode AAD data (hdr_b64u) by setting "out" to NULL int bytes_encrypted = 0; if (EVP_EncryptUpdate(ctx, NULL, &bytes_encrypted, (unsigned char *)jwe->part[0].b64u, jwe->part[0].b64u_len) != 1 || bytes_encrypted != jwe->part[0].b64u_len) { CJOSE_ERROR(err, CJOSE_ERR_CRYPTO); goto _cjose_jwe_encrypt_dat_fail; } // allocate buffer for the ciphertext cjose_get_dealloc()(jwe->part[3].raw); jwe->part[3].raw_len = plaintext_len; if (!_cjose_jwe_malloc(jwe->part[3].raw_len, false, &jwe->part[3].raw, err)) { goto _cjose_jwe_encrypt_dat_fail; } // encrypt entire plaintext to ciphertext buffer if (EVP_EncryptUpdate(ctx, jwe->part[3].raw, &bytes_encrypted, plaintext, plaintext_len) != 1) { CJOSE_ERROR(err, CJOSE_ERR_CRYPTO); goto _cjose_jwe_encrypt_dat_fail; } jwe->part[3].raw_len = bytes_encrypted; // finalize the encryption and set the ciphertext length to correct value if (EVP_EncryptFinal_ex(ctx, NULL, &bytes_encrypted) != 1) { CJOSE_ERROR(err, CJOSE_ERR_CRYPTO); goto _cjose_jwe_encrypt_dat_fail; } // allocate buffer for the authentication tag cjose_get_dealloc()(jwe->part[4].raw); jwe->part[4].raw_len = 16; if (!_cjose_jwe_malloc(jwe->part[4].raw_len, false, &jwe->part[4].raw, err)) { goto _cjose_jwe_encrypt_dat_fail; } // get the GCM-mode authentication tag if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, jwe->part[4].raw_len, jwe->part[4].raw) != 1) { CJOSE_ERROR(err, CJOSE_ERR_CRYPTO); goto _cjose_jwe_encrypt_dat_fail; } EVP_CIPHER_CTX_free(ctx); return true; _cjose_jwe_encrypt_dat_fail: if (NULL != ctx) { EVP_CIPHER_CTX_free(ctx); } return false; }
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_jwk_t *cjose_jwk_create_EC_spec( const cjose_jwk_ec_keyspec *spec, cjose_err *err) { cjose_jwk_t * jwk = NULL; EC_KEY * ec = NULL; EC_GROUP * params = NULL; EC_POINT * Q = NULL; BIGNUM * bnD = NULL; BIGNUM * bnX = NULL; BIGNUM * bnY = NULL; if (!spec) { CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG); return NULL; } bool hasPriv = (NULL != spec->d && 0 < spec->dlen); bool hasPub = ((NULL != spec->x && 0 < spec->xlen) && (NULL != spec->y && 0 < spec->ylen)); if (!hasPriv && !hasPub) { CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG); return NULL; } ec = EC_KEY_new_by_curve_name(spec->crv); if (NULL == ec) { CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG); goto create_EC_failed; } params = (EC_GROUP *)EC_KEY_get0_group(ec); if (NULL == params) { CJOSE_ERROR(err, CJOSE_ERR_NO_MEMORY); goto create_EC_failed; } // convert d from octet string to BIGNUM if (hasPriv) { bnD = BN_bin2bn(spec->d, spec->dlen, NULL); if (NULL == bnD) { CJOSE_ERROR(err, CJOSE_ERR_NO_MEMORY); goto create_EC_failed; } if (1 != EC_KEY_set_private_key(ec, bnD)) { CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG); goto create_EC_failed; } // calculate public key from private Q = EC_POINT_new(params); if (NULL == Q) { CJOSE_ERROR(err, CJOSE_ERR_NO_MEMORY); goto create_EC_failed; } if (1 != EC_POINT_mul(params, Q, bnD, NULL, NULL, NULL)) { CJOSE_ERROR(err, CJOSE_ERR_NO_MEMORY); goto create_EC_failed; } // public key is set below // ignore provided public key! hasPub = false; } if (hasPub) { Q = EC_POINT_new(params); if (NULL == Q) { CJOSE_ERROR(err, CJOSE_ERR_NO_MEMORY); goto create_EC_failed; } bnX = BN_bin2bn(spec->x, spec->xlen, NULL); bnY = BN_bin2bn(spec->y, spec->ylen, NULL); if (!bnX || !bnY) { CJOSE_ERROR(err, CJOSE_ERR_NO_MEMORY); goto create_EC_failed; } if (1 != EC_POINT_set_affine_coordinates_GFp(params, Q, bnX, bnY, NULL)) { CJOSE_ERROR(err, CJOSE_ERR_NO_MEMORY); goto create_EC_failed; } } // always set the public key if (1 != EC_KEY_set_public_key(ec, Q)) { CJOSE_ERROR(err, CJOSE_ERR_NO_MEMORY); goto create_EC_failed; } jwk = _EC_new(spec->crv, ec, err); if (!jwk) { goto create_EC_failed; } // jump to cleanup goto create_EC_cleanup; create_EC_failed: if (jwk) { cjose_get_dealloc()(jwk); jwk = NULL; } if (ec) { EC_KEY_free(ec); ec = NULL; } create_EC_cleanup: if (Q) { EC_POINT_free(Q); Q = NULL; } if (bnD) { BN_free(bnD); bnD = NULL; } if (bnX) { BN_free(bnX); bnX = NULL; } if (bnY) { BN_free(bnY); bnY = NULL; } return jwk; }