in jose/apr_jose_decode.c [828:1183]
apr_status_t apr_jose_decode_json_jws(apr_jose_t **jose, apr_json_value_t *val,
const char *typ, const char *cty, apr_json_value_t *pl,
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 sig64;
apr_jose_text_t pl64;
apr_jose_text_t pls;
apr_jose_jws_t *jws;
apr_json_kv_t *kv;
apr_json_value_t *uh;
apr_bucket *e;
apr_status_t status = APR_EINVAL;
int vflags = APR_JOSE_FLAG_NONE;
if (!cb || !cb->verify) {
apr_errprintf(&(*jose)->result, pool, NULL, 0,
"Verification failed: no verify callback provided");
return APR_EINIT;
}
if (pl->type != APR_JSON_STRING) {
apr_errprintf(&(*jose)->result, pool, NULL, 0,
"Syntax error: JWS 'payload' is not a string");
return APR_EINVAL;
}
pl64.text = pl->value.string.p;
pl64.len = pl->value.string.len;
/*
* Base64url-decode the encoded representation of the JWS Payload,
* following the restriction that no line breaks, whitespace, or
* other additional characters have been used.
*/
pls.text = apr_pdecode_base64(pool, pl64.text,
pl64.len, APR_ENCODE_BASE64URL, &pls.len);
if (!pls.text) {
apr_errprintf(&(*jose)->result, pool, NULL, 0,
"Syntax error: JWS 'payload' is not valid base64url");
return APR_BADCH;
}
*jose = apr_jose_jws_json_make(*jose, NULL, NULL, NULL, pool);
if (!*jose) {
return APR_ENOMEM;
}
jws = (*jose)->jose.jws;
/* for each signature in signatures... */
kv = apr_json_object_get(val, APR_JOSE_JWS_SIGNATURES,
APR_JSON_VALUE_STRING);
if (kv) {
apr_json_value_t *sigs = kv->v;
int i;
int verified = 0;
if (sigs->type != APR_JSON_ARRAY) {
apr_errprintf(&(*jose)->result, pool, NULL, 0,
"Syntax error: JWS 'signatures' is not an array");
return APR_EINVAL;
}
jws->signatures = apr_array_make(pool, sigs->value.array->array->nelts,
sizeof(apr_jose_signature_t *));
if (!jws->signatures) {
return APR_ENOMEM;
}
/*
* If the JWS JSON Serialization is being used, repeat this process
* (steps 4-8) for each digital signature or MAC value contained in
* the representation.
*/
for (i = 0; i < sigs->value.array->array->nelts; i++) {
apr_json_value_t *sig = apr_json_array_get(sigs, i);
if (sig) {
apr_jose_signature_t **sp;
if (sig->type != APR_JSON_OBJECT) {
apr_errprintf(&(*jose)->result, pool, NULL, 0,
"Syntax error: JWS 'signatures' array contains a non-object");
return APR_EINVAL;
}
sp = apr_array_push(jws->signatures);
*sp = apr_pcalloc(pool, sizeof(apr_jose_signature_t));
if (!*sp) {
return APR_ENOMEM;
}
kv = apr_json_object_get(sig, APR_JOSE_JWSE_PROTECTED,
APR_JSON_VALUE_STRING);
if (!kv) {
apr_errprintf(&(*jose)->result, pool, NULL, 0,
"Syntax error: JWS 'protected' header is missing");
return APR_EINVAL;
}
if (kv->v->type != APR_JSON_STRING) {
apr_errprintf(&(*jose)->result, pool, NULL, 0,
"Syntax error: JWS 'protected' is not a string");
return APR_EINVAL;
}
ph64.text = kv->v->value.string.p;
ph64.len = kv->v->value.string.len;
kv = apr_json_object_get(sig, 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: JWS 'header' is not an object");
return APR_EINVAL;
}
uh = kv->v;
}
else {
uh = NULL;
}
kv = apr_json_object_get(sig, APR_JOSE_JWS_SIGNATURE,
APR_JSON_VALUE_STRING);
if (!kv) {
apr_errprintf(&(*jose)->result, pool, NULL, 0,
"Syntax error: JWS 'signature' header is missing");
return APR_EINVAL;
}
if (kv->v->type != APR_JSON_STRING) {
apr_errprintf(&(*jose)->result, pool, NULL, 0,
"Syntax error: JWS 'signature' is not a string");
return APR_EINVAL;
}
sig64.text = kv->v->value.string.p;
sig64.len = kv->v->value.string.len;
/*
* Validate the JWS Signature against the JWS Signing Input
* ASCII(BASE64URL(UTF8(JWS Protected Header)) || '.' ||
* BASE64URL(JWS Payload)) in the manner defined for the algorithm
* being used, which MUST be accurately represented by the value of
* the "alg" (algorithm) Header Parameter, which MUST be present.
* See Section 10.6 for security considerations on algorithm
* validation. Record whether the validation succeeded or not.
*/
apr_brigade_cleanup(bb);
status = apr_brigade_write(bb, NULL, NULL, ph64.text,
ph64.len);
if (APR_SUCCESS != status) {
return status;
}
status = apr_brigade_putc(bb, NULL, NULL, '.');
if (APR_SUCCESS != status) {
return status;
}
status = apr_brigade_write(bb, NULL, NULL, pl64.text,
pl64.len);
if (APR_SUCCESS != status) {
return status;
}
status = apr_jose_decode_jws_signature(jose, *sp, typ, cty,
&ph64, &sig64, &pl64, uh, cb, level, &vflags, pool, bb);
if (APR_SUCCESS == status) {
verified++;
if (verified == 1) {
apr_brigade_cleanup(bb);
e = apr_bucket_pool_create(pls.text, pls.len, pool,
bb->bucket_alloc);
APR_BRIGADE_INSERT_TAIL(bb, e);
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 (!(vflags & APR_JOSE_FLAG_BREAK)) {
break;
}
}
}
if (!verified) {
apr_jose_t *j = *jose;
if (!j->result.msg) {
apr_errprintf(&(*jose)->result, pool, NULL, 0,
"JWS verification failed: no signatures matched");
}
return APR_ENOVERIFY;
}
return APR_SUCCESS;
}
jws->signature = apr_jose_signature_make(NULL, NULL, NULL,
NULL, pool);
if (!jws->signature) {
return APR_ENOMEM;
}
kv = apr_json_object_get(val, APR_JOSE_JWSE_PROTECTED,
APR_JSON_VALUE_STRING);
if (!kv) {
apr_errprintf(&(*jose)->result, pool, NULL, 0,
"Syntax error: JWS 'protected' header is missing");
return APR_EINVAL;
}
if (kv->v->type != APR_JSON_STRING) {
apr_errprintf(&(*jose)->result, pool, NULL, 0,
"Syntax error: JWS 'protected' is not a string");
return APR_EINVAL;
}
ph64.text = kv->v->value.string.p;
ph64.len = kv->v->value.string.len;
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: JWS 'header' is not an object");
return APR_EINVAL;
}
uh = kv->v;
}
else {
uh = NULL;
}
kv = apr_json_object_get(val, APR_JOSE_JWS_SIGNATURE,
APR_JSON_VALUE_STRING);
if (!kv) {
apr_errprintf(&(*jose)->result, pool, NULL, 0,
"Syntax error: JWS 'signature' header is missing");
return APR_EINVAL;
}
if (kv->v->type != APR_JSON_STRING) {
apr_errprintf(&(*jose)->result, pool, NULL, 0,
"Syntax error: JWS 'signature' is not a string");
return APR_EINVAL;
}
sig64.text = kv->v->value.string.p;
sig64.len = kv->v->value.string.len;
/*
* Validate the JWS Signature against the JWS Signing Input
* ASCII(BASE64URL(UTF8(JWS Protected Header)) || '.' ||
* BASE64URL(JWS Payload)) in the manner defined for the algorithm
* being used, which MUST be accurately represented by the value of
* the "alg" (algorithm) Header Parameter, which MUST be present.
* See Section 10.6 for security considerations on algorithm
* validation. Record whether the validation succeeded or not.
*/
apr_brigade_cleanup(bb);
status = apr_brigade_write(bb, NULL, NULL, ph64.text,
ph64.len);
if (APR_SUCCESS != status) {
return status;
}
status = apr_brigade_putc(bb, NULL, NULL, '.');
if (APR_SUCCESS != status) {
return status;
}
status = apr_brigade_write(bb, NULL, NULL, pl64.text,
pl64.len);
if (APR_SUCCESS != status) {
return status;
}
status = apr_jose_decode_jws_signature(jose, jws->signature, typ, cty,
&ph64, &sig64, &pl64, uh, cb, level, &vflags, pool, bb);
if (APR_SUCCESS != status) {
apr_errprintf(&(*jose)->result, pool, NULL, 0,
"JWS verification failed: signature rejected");
return status;
}
apr_brigade_cleanup(bb);
e = apr_bucket_pool_create(pls.text, pls.len, pool,
bb->bucket_alloc);
APR_BRIGADE_INSERT_TAIL(bb, e);
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.jws->payload : jose, typ, bb, cb,
level, flags, pool);
}