apr_status_t apr_jose_decode_compact()

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;
}