apr_status_t apr_jose_decode_json_jwe()

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;

}