apr_status_t apr_jose_decode_json_jws()

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