in jose/apr_jose_decode.c [1186:1588]
apr_status_t apr_jose_decode_json_jwe(apr_jose_t **jose, apr_json_value_t *val,
const char *typ, const char *cty, apr_json_value_t *ct,
apr_jose_cb_t *cb, int level, int flags, apr_pool_t *pool,
apr_bucket_brigade *bb)
{
apr_jose_text_t ph64;
apr_jose_text_t aad64;
apr_jose_jwe_t *jwe;
apr_json_kv_t *kv;
apr_status_t status = APR_EGENERAL;
int dflags = APR_JOSE_FLAG_NONE;
if (!cb || !cb->decrypt) {
apr_errprintf(&(*jose)->result, pool, NULL, 0,
"Decryption failed: no decrypt callback provided");
return APR_EINIT;
}
if (ct->type != APR_JSON_STRING) {
apr_errprintf(&(*jose)->result, pool, NULL, 0,
"Syntax error: JWE 'ciphertext' is not a string");
return APR_EINVAL;
}
*jose = apr_jose_jwe_json_make(*jose, NULL, NULL, NULL, NULL, pool);
if (!*jose) {
return APR_ENOMEM;
}
jwe = (*jose)->jose.jwe;
jwe->encryption = apr_jose_encryption_make(NULL, NULL,
NULL, pool);
if (!jwe->encryption) {
return APR_ENOMEM;
}
/*
* Base64url decode the encoded representations of the JWE
* Protected Header, the JWE Encrypted Key, the JWE Initialization
* Vector, the JWE Ciphertext, the JWE Authentication Tag, and the
* JWE AAD, following the restriction that no line breaks,
* whitespace, or other additional characters have been used.
*/
kv = apr_json_object_get(val, APR_JOSE_JWSE_PROTECTED,
APR_JSON_VALUE_STRING);
if (kv) {
const char *phs;
apr_size_t phlen;
apr_off_t offset;
if (kv->v->type != APR_JSON_STRING) {
apr_errprintf(&(*jose)->result, pool, NULL, 0,
"Syntax error: JWE 'protected' is not a string");
return APR_EINVAL;
}
/*
* Verify that the octet sequence resulting from decoding the
* encoded JWE Protected Header is a UTF-8-encoded representation
* of a completely valid JSON object conforming to RFC 7159
* [RFC7159]; let the JWE Protected Header be this JSON object.
*/
ph64.text = kv->v->value.string.p;
ph64.len = kv->v->value.string.len;
phs = apr_pdecode_base64(pool, ph64.text,
ph64.len, APR_ENCODE_BASE64URL, &phlen);
if (!phs) {
apr_errprintf(&(*jose)->result, pool, NULL, 0,
"Syntax error: JWE 'protected' is not valid base64url");
return APR_EINVAL;
}
status = apr_json_decode(&jwe->encryption->protected, phs, phlen, &offset,
APR_JSON_FLAGS_WHITESPACE, level, pool);
if (APR_SUCCESS != status) {
char buf[1024];
apr_strerror(status, buf, sizeof(buf));
apr_errprintf(&(*jose)->result, pool, NULL, 0,
"Syntax error: JWE 'protected' decoding failed at %" APR_OFF_T_FMT ": %s",
offset, buf);
return status;
}
}
else {
apr_errprintf(&(*jose)->result, pool, NULL, 0,
"Syntax error: JWE 'protected' header is missing");
return APR_EINVAL;
}
/* unprotected */
kv = apr_json_object_get(val, APR_JOSE_JWE_UNPROTECTED,
APR_JSON_VALUE_STRING);
if (kv) {
if (kv->v->type != APR_JSON_OBJECT) {
apr_errprintf(&(*jose)->result, pool, NULL, 0,
"Syntax error: JWE 'unprotected' is not an object");
return APR_EINVAL;
}
jwe->encryption->unprotected = kv->v;
}
/* ciphertext */
jwe->encryption->cipher.data = apr_pdecode_base64_binary(pool,
ct->value.string.p, ct->value.string.len, APR_ENCODE_BASE64URL,
&jwe->encryption->cipher.len);
if (!jwe->encryption->cipher.data) {
apr_errprintf(&(*jose)->result, pool, NULL, 0,
"Syntax error: JWE 'ciphertext' is not valid base64url");
return APR_BADCH;
}
/* iv */
kv = apr_json_object_get(val, APR_JOSE_JWE_IV, APR_JSON_VALUE_STRING);
if (kv) {
if (kv->v->type != APR_JSON_STRING) {
apr_errprintf(&(*jose)->result, pool, NULL, 0,
"Syntax error: JWE 'iv' is not a string");
return APR_EINVAL;
}
jwe->encryption->iv.data = apr_pdecode_base64_binary(pool,
kv->v->value.string.p, kv->v->value.string.len, APR_ENCODE_BASE64URL,
&jwe->encryption->iv.len);
if (!jwe->encryption->iv.data) {
apr_errprintf(&(*jose)->result, pool, NULL, 0,
"Syntax error: JWE 'iv' is not valid base64url");
return APR_BADCH;
}
}
/* tag */
kv = apr_json_object_get(val, APR_JOSE_JWE_TAG, APR_JSON_VALUE_STRING);
if (kv) {
if (kv->v->type != APR_JSON_STRING) {
apr_errprintf(&(*jose)->result, pool, NULL, 0,
"Syntax error: JWE 'tag' is not a string");
return APR_EINVAL;
}
jwe->encryption->tag.data = apr_pdecode_base64_binary(pool,
kv->v->value.string.p, kv->v->value.string.len, APR_ENCODE_BASE64URL,
&jwe->encryption->tag.len);
if (!jwe->encryption->tag.data) {
apr_errprintf(&(*jose)->result, pool, NULL, 0,
"Syntax error: JWE 'tag' is not valid base64url");
return APR_BADCH;
}
}
/* aad */
kv = apr_json_object_get(val, APR_JOSE_JWE_AAD, APR_JSON_VALUE_STRING);
if (kv) {
if (kv->v->type != APR_JSON_STRING) {
apr_errprintf(&(*jose)->result, pool, NULL, 0,
"Syntax error: JWE 'aad' is not a string");
return APR_EINVAL;
}
aad64.text = kv->v->value.string.p;
aad64.len = kv->v->value.string.len;
jwe->encryption->aad.data = apr_pdecode_base64_binary(pool,
aad64.text, aad64.len, APR_ENCODE_BASE64URL,
&jwe->encryption->aad.len);
if (!jwe->encryption->aad.data) {
apr_errprintf(&(*jose)->result, pool, NULL, 0,
"Syntax error: JWE 'add' is not valid base64url");
return APR_BADCH;
}
}
else {
memset(&aad64, 0, sizeof(apr_jose_text_t));
}
/* for each recipient in recipients... */
kv = apr_json_object_get(val, APR_JOSE_JWE_RECIPIENTS,
APR_JSON_VALUE_STRING);
if (kv) {
apr_json_value_t *recips = kv->v;
int i;
int decrypt = 0;
if (recips->type != APR_JSON_ARRAY) {
apr_errprintf(&(*jose)->result, pool, NULL, 0,
"Syntax error: JWE 'recipients' is not an array");
return APR_EINVAL;
}
(*jose)->jose.jwe->recipients = apr_array_make(pool,
recips->value.array->array->nelts, sizeof(apr_jose_recipient_t *));
if (!(*jose)->jose.jwe->recipients) {
return APR_ENOMEM;
}
for (i = 0; i < recips->value.array->array->nelts; i++) {
apr_json_value_t *recip = apr_json_array_get(recips, i);
if (recip) {
apr_jose_recipient_t **rp;
apr_jose_recipient_t *recipient;
if (recip->type != APR_JSON_OBJECT) {
apr_errprintf(&(*jose)->result, pool, NULL, 0,
"Syntax error: JWE 'recipients' array contains a non-object");
return APR_EINVAL;
}
rp = apr_array_push((*jose)->jose.jwe->recipients);
*rp = recipient = apr_pcalloc(pool, sizeof(apr_jose_recipient_t));
if (!recipient) {
return APR_ENOMEM;
}
/* unprotected */
kv = apr_json_object_get(recip, APR_JOSE_JWSE_HEADER,
APR_JSON_VALUE_STRING);
if (kv) {
if (kv->v->type != APR_JSON_OBJECT) {
apr_errprintf(&(*jose)->result, pool, NULL, 0,
"Syntax error: JWE 'header' is not an object");
return APR_EINVAL;
}
recipient->header = kv->v;
}
kv = apr_json_object_get(recip, APR_JOSE_JWE_EKEY,
APR_JSON_VALUE_STRING);
if (kv) {
if (kv->v->type != APR_JSON_STRING) {
apr_errprintf(&(*jose)->result, pool, NULL, 0,
"Syntax error: JWE 'encrypted_key' element must be a string");
return APR_EINVAL;
}
recipient->ekey.data = apr_pdecode_base64_binary(pool,
kv->v->value.string.p, kv->v->value.string.len, APR_ENCODE_BASE64URL,
&recipient->ekey.len);
if (!recipient->ekey.data) {
apr_errprintf(&(*jose)->result, pool, NULL, 0,
"Syntax error: JWE 'encrypted_key' is not valid base64url");
return APR_BADCH;
}
}
apr_brigade_cleanup(bb);
status = apr_jose_decode_jwe_recipient(jose, bb, recipient,
jwe->encryption, typ, cty, &ph64, &aad64, cb, level,
&dflags, pool);
if (APR_SUCCESS == status) {
decrypt++;
if (decrypt == 1) {
if (level <= 0) {
apr_errprintf(&(*jose)->result, pool, NULL, 0,
"Syntax error: too many nested JOSE payloads");
return APR_EINVAL;
}
level--;
status = apr_jose_decode(
flags & APR_JOSE_FLAG_DECODE_ALL ?
&(*jose)->jose.jwe->payload : jose, typ,
bb, cb, level, flags, pool);
if (APR_SUCCESS != status) {
return status;
}
}
}
if (!(dflags & APR_JOSE_FLAG_BREAK)) {
break;
}
}
}
if (!decrypt) {
apr_jose_t *j = *jose;
if (!j->result.msg) {
apr_errprintf(&(*jose)->result, pool, NULL, 0,
"JWE decryption failed: no recipients matched");
}
return APR_ECRYPT;
}
return APR_SUCCESS;
}
/* ok, just one recipient */
kv = apr_json_object_get(val, APR_JOSE_JWE_EKEY, APR_JSON_VALUE_STRING);
if (kv) {
apr_json_value_t *ekey = kv->v;
apr_jose_recipient_t *recipient;
if (ekey->type != APR_JSON_STRING) {
apr_errprintf(&(*jose)->result, pool, NULL, 0,
"Syntax error: JWE 'encrypted_key' element must be a string");
return APR_EINVAL;
}
recipient = apr_pcalloc(pool, sizeof(apr_jose_recipient_t));
if (!recipient) {
return APR_ENOMEM;
}
/* unprotected */
kv = apr_json_object_get(val, APR_JOSE_JWSE_HEADER,
APR_JSON_VALUE_STRING);
if (kv) {
if (kv->v->type != APR_JSON_OBJECT) {
apr_errprintf(&(*jose)->result, pool, NULL, 0,
"Syntax error: JWE 'header' is not an object");
return APR_EINVAL;
}
recipient->header = kv->v;
}
apr_brigade_cleanup(bb);
status = apr_jose_decode_jwe_recipient(jose, bb, recipient,
jwe->encryption, typ, cty, &ph64, &aad64, cb, level, &dflags,
pool);
if (APR_SUCCESS == status) {
if (level <= 0) {
apr_errprintf(&(*jose)->result, pool, NULL, 0,
"Syntax error: too many nested JOSE payloads");
return APR_EINVAL;
}
level--;
return apr_jose_decode(
flags & APR_JOSE_FLAG_DECODE_ALL ?
&(*jose)->jose.jwe->payload : jose, typ, bb,
cb, level, flags, pool);
}
if (APR_SUCCESS != status) {
apr_errprintf(&(*jose)->result, pool, NULL, 0,
"Decryption failed: JWE decryption failed");
}
}
/* no recipient at all */
apr_errprintf(&(*jose)->result, pool, NULL, 0,
"Syntax error: No 'recipients' or 'encrypted_key' present");
return APR_EINVAL;
}