in jose/apr_jose_decode.c [646:825]
apr_status_t apr_jose_decode_compact(apr_jose_t **jose, const char *typ,
apr_bucket_brigade *brigade, apr_jose_cb_t *cb, int level, int flags,
apr_pool_t *pool)
{
apr_bucket_brigade *bb;
apr_jose_text_t in;
apr_jose_text_t ph64;
apr_jose_text_t phs;
apr_json_kv_t *kv;
apr_json_value_t *header;
const char *left;
const char *right;
const char *dot;
const char *cty = NULL;
apr_off_t offset;
apr_status_t status = APR_ENOTIMPL;
status = apr_jose_flatten(brigade, &in, pool);
if (APR_SUCCESS != status) {
return status;
}
left = in.text;
right = in.text + in.len;
*jose = apr_jose_make(NULL, APR_JOSE_TYPE_NONE, pool);
if (!*jose) {
return APR_ENOMEM;
}
bb = apr_brigade_create(pool, brigade->bucket_alloc);
if (!bb) {
return APR_ENOMEM;
}
/*
* Use a heuristic to see whether this is a JWT, JWE or JWS.
*
* This is described in https://tools.ietf.org/html/rfc7519#section-7.2
*/
/* Verify that the JWT contains at least one period ('.')
* character.
*/
dot = memchr(left, '.', in.len);
if (!dot) {
apr_errprintf(&(*jose)->result, pool, NULL, 0,
"Syntax error: JOSE compact decoding failed: no dots found");
return APR_BADCH;
}
ph64.text = in.text;
ph64.len = dot - in.text;
left = dot + 1;
/*
* Let the Encoded JOSE Header be the portion of the JWT before the
* first period ('.') character.
*
* Base64url decode the Encoded JOSE Header following the
* restriction that no line breaks, whitespace, or other additional
* characters have been used.
*/
phs.text = apr_pdecode_base64(pool, ph64.text, ph64.len, APR_ENCODE_BASE64URL,
&phs.len);
if (!phs.text) {
apr_errprintf(&(*jose)->result, pool, NULL, 0,
"Syntax error: JOSE header base64url decoding failed at %" APR_SIZE_T_FMT "",
phs.len);
return APR_BADCH;
}
/*
* Verify that the resulting octet sequence is a UTF-8-encoded
* representation of a completely valid JSON object conforming to
* RFC 7159 [RFC7159]; let the JOSE Header be this JSON object.
*/
status = apr_json_decode(&header, phs.text, phs.len, &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: JOSE header decoding failed at %" APR_OFF_T_FMT ": %s",
offset, buf);
return status;
}
kv = apr_json_object_get(header, APR_JOSE_JWSE_CONTENT_TYPE,
APR_JSON_VALUE_STRING);
if (kv) {
if (kv->v->type == APR_JSON_STRING) {
cty = apr_pstrndup(pool, kv->v->value.string.p,
kv->v->value.string.len);
}
}
if (cty) {
if (!strcasecmp(cty, "JWT") || !strcasecmp(cty, "application/jwt")) {
typ = "JWT";
}
}
kv = apr_json_object_get(header, APR_JOSE_JWSE_TYPE, APR_JSON_VALUE_STRING);
if (kv) {
if (kv->v->type == APR_JSON_STRING) {
typ = apr_pstrndup(pool, kv->v->value.string.p,
kv->v->value.string.len);
}
}
/*
* Determine whether the JWT is a JWS or a JWE using any of the
* methods described in Section 9 of [JWE].
*
* The JOSE Header for a JWS can also be distinguished from the JOSE
* Header for a JWE by determining whether an "enc" (encryption
* algorithm) member exists. If the "enc" member exists, it is a
* JWE; otherwise, it is a JWS.
*/
kv = apr_json_object_get(header, APR_JOSE_JWE_ENCRYPTION,
APR_JSON_VALUE_STRING);
if (kv) {
status = apr_jose_decode_compact_jwe(jose, left, right, header, kv->v,
typ, cty, &ph64, cb, level, flags, pool, bb);
} else {
status = apr_jose_decode_compact_jws(jose, left, right, header, typ, cty, &in, &ph64,
cb, level, flags, pool, bb);
}
if (APR_SUCCESS == status) {
/*
* JWT is an anomaly.
*
* If we have stripped off one level of JOSE, and the content-type
* is present and set to JWT, our payload is a next level JOSE.
*
* If we have stripped off one level of JOSE, and the content-type
* is not present but the type is present and set to JWT, our payload
* is a JSON object containing claims.
*/
if (!cty && typ
&& (!strcasecmp(typ, "JWT")
|| !strcasecmp(typ, "application/jwt"))) {
status = apr_jose_decode_jwt(
flags & APR_JOSE_FLAG_DECODE_ALL ?
&(*jose)->jose.jws->payload : jose, typ, bb, cb,
level, flags, pool);
}
else {
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.jws->payload : jose, typ, bb, cb,
level, flags, pool);
}
}
return status;
}