blob: af10e35a486194b96b9b0db79c02d85a5cdacf33 [file] [log] [blame]
/* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* levelations under the License.
*/
#include "apr_jose.h"
#include "apr_lib.h"
#include "apr_encode.h"
static
apr_status_t apr_jose_flatten(apr_bucket_brigade *bb, apr_jose_text_t *in,
apr_pool_t *pool)
{
apr_bucket *e;
/* most common case - one pool bucket, avoid unnecessary duplication */
e = APR_BRIGADE_FIRST(bb);
if (e != APR_BRIGADE_SENTINEL(bb)) {
if (!APR_BUCKET_NEXT(e) && APR_BUCKET_IS_POOL(e)) {
apr_bucket_pool *p = e->data;
if (pool == p->pool) {
return apr_bucket_read(e, &in->text, &in->len, APR_BLOCK_READ);
}
}
}
return apr_brigade_pflatten(bb, (char **)&in->text, &in->len, pool);
}
static
apr_status_t apr_jose_decode_jwk(apr_jose_t **jose,
const char *typ, apr_bucket_brigade *bb, apr_jose_cb_t *cb,
int level, int flags, apr_pool_t *pool)
{
apr_json_value_t *key;
apr_jose_text_t in;
apr_off_t offset;
apr_status_t status;
status = apr_jose_flatten(bb, &in, pool);
if (APR_SUCCESS != status) {
return status;
}
status = apr_json_decode(&key, in.text, in.len, &offset,
APR_JSON_FLAGS_WHITESPACE, level, pool);
*jose = apr_jose_jwk_make(NULL, key, pool);
if (!*jose) {
return APR_ENOMEM;
}
if (APR_SUCCESS != status) {
char buf[1024];
apr_strerror(status, buf, sizeof(buf));
apr_errprintf(&(*jose)->result, pool, NULL, 0,
"Syntax error: JWK decoding failed at offset %" APR_OFF_T_FMT ": %s",
offset, buf);
return status;
}
return APR_SUCCESS;
}
static
apr_status_t apr_jose_decode_jwks(apr_jose_t **jose,
const char *typ, apr_bucket_brigade *bb, apr_jose_cb_t *cb,
int level, int flags, apr_pool_t *pool)
{
apr_json_value_t *keys;
apr_jose_text_t in;
apr_off_t offset;
apr_status_t status;
status = apr_jose_flatten(bb, &in, pool);
if (APR_SUCCESS != status) {
return status;
}
status = apr_json_decode(&keys, in.text, in.len,
&offset, APR_JSON_FLAGS_WHITESPACE, level, pool);
*jose = apr_jose_jwks_make(NULL, keys, pool);
if (!*jose) {
return APR_ENOMEM;
}
if (APR_SUCCESS != status) {
char buf[1024];
apr_strerror(status, buf, sizeof(buf));
apr_errprintf(&(*jose)->result, pool, NULL, 0,
"Syntax error: JWKS decoding failed at offset %" APR_OFF_T_FMT ": %s",
offset, buf);
return status;
}
if (keys->type != APR_JSON_ARRAY) {
apr_errprintf(&(*jose)->result, pool, NULL, 0,
"Syntax error: JWKS 'keys' is not an array");
return APR_EINVAL;
}
return APR_SUCCESS;
}
static
apr_status_t apr_jose_decode_jwt(apr_jose_t **jose,
const char *typ, apr_bucket_brigade *bb, apr_jose_cb_t *cb,
int level, int flags, apr_pool_t *pool)
{
apr_json_value_t *claims;
apr_jose_text_t in;
apr_off_t offset;
apr_status_t status;
status = apr_jose_flatten(bb, &in, pool);
if (APR_SUCCESS != status) {
return status;
}
status = apr_json_decode(&claims, in.text, in.len, &offset,
APR_JSON_FLAGS_WHITESPACE, level, pool);
*jose = apr_jose_jwt_make(NULL, claims, pool);
if (!*jose) {
return APR_ENOMEM;
}
if (APR_SUCCESS != status) {
char buf[1024];
apr_strerror(status, buf, sizeof(buf));
apr_errprintf(&(*jose)->result, pool, NULL, 0,
"Syntax error: JWT decoding failed at offset %" APR_OFF_T_FMT ": %s",
offset, buf);
return status;
}
return APR_SUCCESS;
}
static
apr_status_t apr_jose_decode_data(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_jose_text_t in;
apr_status_t status;
status = apr_jose_flatten(brigade, &in, pool);
if (APR_SUCCESS != status) {
return status;
}
*jose = apr_jose_data_make(NULL, typ, (const unsigned char *) in.text,
in.len, pool);
if (!*jose) {
return APR_ENOMEM;
}
return status;
}
static
apr_status_t apr_jose_decode_jws_signature(apr_jose_t **jose,
apr_jose_signature_t *signature, const char *typ, const char *cty,
apr_jose_text_t *ph64, apr_jose_text_t *sig64, apr_jose_text_t *pl64,
apr_json_value_t *uh, apr_jose_cb_t *cb, int level, int *flags,
apr_pool_t *pool, apr_bucket_brigade *bb)
{
const char *phs;
apr_size_t phlen;
apr_off_t offset;
apr_status_t status = APR_SUCCESS;
/*
* Base64url-decode the encoded representation of the JWS Protected
* Header, following the restriction that no line breaks,
* whitespace, or other additional characters have been used.
*/
phs = apr_pdecode_base64(pool, ph64->text, ph64->len, APR_ENCODE_BASE64URL,
&phlen);
if (!phs) {
apr_errprintf(&(*jose)->result, pool, NULL, 0,
"Syntax error: JWS 'protected' is not valid base64url");
return APR_EINVAL;
}
/*
* 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 JWS Protected Header be this JSON
* object.
*/
status = apr_json_decode(&signature->protected_header, 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: JWS 'protected' decoding failed at %" APR_OFF_T_FMT ": %s",
offset, buf);
return status;
}
/*
* If using the JWS Compact Serialization, let the JOSE Header be
* the JWS Protected Header. Otherwise, when using the JWS JSON
* Serialization, let the JOSE Header be the union of the members of
* the corresponding JWS Protected Header and JWS Unprotected
* Header, all of which must be completely valid JSON objects.
* During this step, verify that the resulting JOSE Header does not
* contain duplicate Header Parameter names. When using the JWS
* JSON Serialization, this restriction includes that the same
* Header Parameter name also MUST NOT occur in distinct JSON object
* values that together comprise the JOSE Header.
*/
if (uh) {
signature->header = apr_json_overlay(pool, signature->protected_header,
uh, APR_JSON_FLAGS_STRICT);
} else {
signature->header = signature->protected_header;
}
/*
* Verify that the implementation understands and can process all
* fields that it is required to support, whether required by this
* specification, by the algorithm being used, or by the "crit"
* Header Parameter value, and that the values of those parameters
* are also understood and supported.
*/
/*
* Base64url-decode the encoded representation of the JWS Signature,
* following the restriction that no line breaks, whitespace, or
* other additional characters have been used.
*/
signature->sig.data = apr_pdecode_base64_binary(pool, sig64->text,
sig64->len,
APR_ENCODE_BASE64URL, &signature->sig.len);
if (!signature->sig.data) {
apr_errprintf(&(*jose)->result, pool, NULL, 0,
"Syntax error: JWS signature decoding failed: bad character");
return APR_BADCH;
}
/*
* The verify function is expected to perform some or all of the
* following steps:
*
* FIXME fill in from RFC
*/
status = cb->verify(bb, *jose, signature, cb->ctx, flags, pool);
return status;
}
static
apr_status_t apr_jose_decode_jwe_recipient(apr_jose_t **jose,
apr_bucket_brigade *bb, apr_jose_recipient_t *recipient,
apr_jose_encryption_t *encryption, const char *typ, const char *cty,
apr_jose_text_t *ph64, apr_jose_text_t *aad64, apr_jose_cb_t *cb,
int level, int *dflags, apr_pool_t *pool)
{
apr_json_value_t *header;
apr_status_t status;
/*
* If using the JWE Compact Serialization, let the JOSE Header be
* the JWE Protected Header. Otherwise, when using the JWE JSON
* Serialization, let the JOSE Header be the union of the members
* of the JWE Protected Header, the JWE Shared Unprotected Header
* and the corresponding JWE Per-Recipient Unprotected Header, all
* of which must be completely valid JSON objects. During this
* step, verify that the resulting JOSE Header does not contain
* duplicate Header Parameter names. When using the JWE JSON
* Serialization, this restriction includes that the same Header
* Parameter name also MUST NOT occur in distinct JSON object
* values that together comprise the JOSE Header.
*/
header = apr_json_overlay(pool, recipient->header,
apr_json_overlay(pool, encryption->protected,
encryption->unprotected, APR_JSON_FLAGS_STRICT),
APR_JSON_FLAGS_STRICT);
if (!header) {
apr_errprintf(&(*jose)->result, pool, NULL, 0,
"JWE decryption failed: protected, unprotected and per "
"recipient headers had an overlapping element, or were all missing");
return APR_EINVAL;
}
/*
* Verify that the implementation understands and can process all
* fields that it is required to support, whether required by this
* specification, by the algorithms being used, or by the "crit"
* Header Parameter value, and that the values of those parameters
* are also understood and supported.
*/
/*
* The decrypt function is expected to perform some or all of the
* following steps:
*
* 6. Determine the Key Management Mode employed by the algorithm
* specified by the "alg" (algorithm) Header Parameter.
*
* 7. Verify that the JWE uses a key known to the recipient.
*
* 8. When Direct Key Agreement or Key Agreement with Key Wrapping are
* employed, use the key agreement algorithm to compute the value
* of the agreed upon key. When Direct Key Agreement is employed,
* let the CEK be the agreed upon key. When Key Agreement with Key
* Wrapping is employed, the agreed upon key will be used to
* decrypt the JWE Encrypted Key.
*
* 9. When Key Wrapping, Key Encryption, or Key Agreement with Key
* Wrapping are employed, decrypt the JWE Encrypted Key to produce
* the CEK. The CEK MUST have a length equal to that required for
* the content encryption algorithm. Note that when there are
* multiple recipients, each recipient will only be able to decrypt
* JWE Encrypted Key values that were encrypted to a key in that
* recipient's possession. It is therefore normal to only be able
* to decrypt one of the per-recipient JWE Encrypted Key values to
* obtain the CEK value. Also, see Section 11.5 for security
* considerations on mitigating timing attacks.
*
* 10. When Direct Key Agreement or Direct Encryption are employed,
* verify that the JWE Encrypted Key value is an empty octet
* sequence.
*
* 11. When Direct Encryption is employed, let the CEK be the shared
* symmetric key.
*
* 12. Record whether the CEK could be successfully determined for this
* recipient or not.
*
* 13. If the JWE JSON Serialization is being used, repeat this process
* (steps 4-12) for each recipient contained in the representation.
*
* 14. Compute the Encoded Protected Header value BASE64URL(UTF8(JWE
* Protected Header)). If the JWE Protected Header is not present
* (which can only happen when using the JWE JSON Serialization and
* no "protected" member is present), let this value be the empty
* string.
*
* 15. Let the Additional Authenticated Data encryption parameter be
* ASCII(Encoded Protected Header). However, if a JWE AAD value is
* present (which can only be the case when using the JWE JSON
* Serialization), instead let the Additional Authenticated Data
* encryption parameter be ASCII(Encoded Protected Header || '.' ||
* BASE64URL(JWE AAD)).
*
* 16. Decrypt the JWE Ciphertext using the CEK, the JWE Initialization
* Vector, the Additional Authenticated Data value, and the JWE
* Authentication Tag (which is the Authentication Tag input to the
* calculation) using the specified content encryption algorithm,
* returning the decrypted plaintext and validating the JWE
* Authentication Tag in the manner specified for the algorithm,
* rejecting the input without emitting any decrypted output if the
* JWE Authentication Tag is incorrect.
*
* 17. If a "zip" parameter was included, uncompress the decrypted
* plaintext using the specified compression algorithm.
*/
status = cb->decrypt(bb, *jose, recipient, encryption, header, ph64, aad64,
cb->ctx, dflags, pool);
recipient->status = status;
return status;
}
static
apr_status_t apr_jose_decode_compact_jws(apr_jose_t **jose,
const char *left, const char *right,
apr_json_value_t *ph, const char *typ, const char *cty,
apr_jose_text_t *in, apr_jose_text_t *ph64, apr_jose_cb_t *cb,
int level, int flags, apr_pool_t *pool, apr_bucket_brigade *bb)
{
apr_jose_jws_t *jws;
apr_jose_text_t sig64;
apr_jose_text_t pl64;
apr_jose_text_t pls;
const char *dot;
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;
}
*jose = apr_jose_jws_make(*jose, NULL, NULL, NULL, pool);
if (!*jose) {
return APR_ENOMEM;
}
jws = (*jose)->jose.jws;
/*
* If using the JWS Compact Serialization, let the JOSE Header be
* the JWS Protected Header.
*/
jws->signature = apr_jose_signature_make(NULL, NULL, ph, NULL, pool);
if (!jws->signature) {
return APR_ENOMEM;
}
dot = memchr(left, '.', right - left);
if (!dot) {
apr_errprintf(&(*jose)->result, pool, NULL, 0,
"Syntax error: JWS compact decoding failed: one lonely dot");
return APR_BADCH;
}
pl64.text = left;
pl64.len = dot - left;
left = dot + 1;
sig64.text = left;
sig64.len = right - left;
/*
* 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.
*/
status = apr_brigade_write(bb, NULL, NULL, in->text,
sig64.text - in->text - 1);
if (APR_SUCCESS != status) {
return status;
}
status = apr_jose_decode_jws_signature(jose, jws->signature,
typ, cty, ph64, &sig64, &pl64, NULL, cb, level, &vflags, pool, bb);
if (APR_SUCCESS != status) {
apr_errprintf(&(*jose)->result, pool, NULL, 0,
"JWS verification failed: signature rejected");
return status;
}
/*
* 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;
}
apr_brigade_cleanup(bb);
e = apr_bucket_pool_create(pls.text, pls.len, pool,
bb->bucket_alloc);
APR_BRIGADE_INSERT_TAIL(bb, e);
return APR_SUCCESS;
}
static
apr_status_t apr_jose_decode_compact_jwe(apr_jose_t **jose, const char *left,
const char *right, apr_json_value_t *ph, apr_json_value_t *enc,
const char *typ, const char *cty, apr_jose_text_t *ph64,
apr_jose_cb_t *cb, int level, int flags, apr_pool_t *pool,
apr_bucket_brigade *bb)
{
const char *dot;
apr_jose_jwe_t *jwe;
apr_jose_text_t aad64;
apr_status_t status;
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;
}
*jose = apr_jose_jwe_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;
}
jwe->recipient = apr_jose_recipient_make(NULL, NULL, NULL, pool);
if (!jwe->recipient) {
return APR_ENOMEM;
}
/*
* Parse the JWE representation to extract the serialized values
* for the components of the JWE. When using the JWE Compact
* Serialization, these components are the base64url-encoded
* representations of the JWE Protected Header, the JWE Encrypted
* Key, the JWE Initialization Vector, the JWE Ciphertext, and the
* JWE Authentication Tag, and when using the JWE JSON
* Serialization, these components also include the base64url-
* encoded representation of the JWE AAD and the unencoded JWE
* Shared Unprotected Header and JWE Per-Recipient Unprotected
* Header values. When using the JWE Compact Serialization, the
* JWE Protected Header, the JWE Encrypted Key, the JWE
* Initialization Vector, the JWE Ciphertext, and the JWE
* Authentication Tag are represented as base64url-encoded values
* in that order, with each value being separated from the next by
* a single period ('.') character, resulting in exactly four
* delimiting period characters being used. The JWE JSON
* Serialization is described in Section 7.2.
*/
/* protected header */
if (ph) {
jwe->encryption->protected = ph;
}
/* encrypted key */
dot = memchr(left, '.', right - left);
if (!dot) {
apr_errprintf(&(*jose)->result, pool, NULL, 0,
"Syntax error: compact JWE decoding failed: one lonely dot");
return APR_BADCH;
}
jwe->recipient->ekey.data = apr_pdecode_base64_binary(pool, left,
dot - left, APR_ENCODE_BASE64URL, &jwe->recipient->ekey.len);
if (!jwe->recipient->ekey.data) {
apr_errprintf(&(*jose)->result, pool, NULL, 0,
"Syntax error: JWE ekey base64url decoding failed at %" APR_SIZE_T_FMT "",
jwe->recipient->ekey.len);
return APR_BADCH;
}
left = dot + 1;
/* iv */
dot = memchr(left, '.', right - left);
if (!dot) {
apr_errprintf(&(*jose)->result, pool, NULL, 0,
"Syntax error: JWE compact decoding failed: only two dots");
return APR_BADCH;
}
jwe->encryption->iv.data = apr_pdecode_base64_binary(pool, left,
dot - left, APR_ENCODE_BASE64URL, &jwe->encryption->iv.len);
if (!jwe->encryption->iv.data) {
apr_errprintf(&(*jose)->result, pool, NULL, 0,
"Syntax error: JWE iv base64url decoding failed at %" APR_SIZE_T_FMT "",
jwe->encryption->iv.len);
return APR_BADCH;
}
left = dot + 1;
/* ciphertext */
dot = memchr(left, '.', right - left);
if (!dot) {
apr_errprintf(&(*jose)->result, pool, NULL, 0,
"Syntax error: JOSE compact JWE decoding failed: only three dots");
return APR_BADCH;
}
jwe->encryption->cipher.data = apr_pdecode_base64_binary(pool, left,
dot - left, APR_ENCODE_BASE64URL, &jwe->encryption->cipher.len);
if (!jwe->encryption->cipher.data) {
apr_errprintf(&(*jose)->result, pool, NULL, 0,
"Syntax error: JWE ciphertext base64url decoding failed at %" APR_SIZE_T_FMT "",
jwe->encryption->cipher.len);
return APR_BADCH;
}
left = dot + 1;
/* tag */
jwe->encryption->tag.data = apr_pdecode_base64_binary(pool, left,
dot - left, APR_ENCODE_BASE64URL, &jwe->encryption->tag.len);
if (!jwe->encryption->tag.data) {
apr_errprintf(&(*jose)->result, pool, NULL, 0,
"Syntax error: JWE tag base64url decoding failed at %" APR_SIZE_T_FMT "",
jwe->encryption->tag.len);
return APR_BADCH;
}
/* aad is the empty string in compact serialisation */
memset(&aad64, 0, sizeof(apr_jose_text_t));
status = apr_jose_decode_jwe_recipient(jose,
bb, jwe->recipient, jwe->encryption, typ, cty, ph64, &aad64, cb,
level, &dflags, pool);
if (APR_SUCCESS != status) {
apr_errprintf(&(*jose)->result, pool, NULL, 0,
"Decryption failed: JWE decryption failed");
return status;
}
return APR_SUCCESS;
}
static
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;
}
static
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);
}
static
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;
}
static
apr_status_t apr_jose_decode_json(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_json_value_t *val;
apr_bucket_brigade *bb;
apr_jose_text_t in;
apr_off_t offset;
apr_status_t status;
*jose = apr_jose_make(NULL, APR_JOSE_TYPE_NONE, pool);
if (!*jose) {
return APR_ENOMEM;
}
status = apr_jose_flatten(brigade, &in, pool);
if (APR_SUCCESS != status) {
return status;
}
bb = apr_brigade_create(pool, brigade->bucket_alloc);
if (!bb) {
return APR_ENOMEM;
}
/*
* Parse the JWS representation to extract the serialized values for
* the components of the JWS. When using the JWS Compact
* Serialization, these components are the base64url-encoded
* representations of the JWS Protected Header, the JWS Payload, and
* the JWS Signature, and when using the JWS JSON Serialization,
* these components also include the unencoded JWS Unprotected
* Header value. When using the JWS Compact Serialization, the JWS
* Protected Header, the JWS Payload, and the JWS Signature are
* represented as base64url-encoded values in that order, with each
* value being separated from the next by a single period ('.')
* character, resulting in exactly two delimiting period characters
* being used. The JWS JSON Serialization is described in
* Section 7.2.
*/
status = apr_json_decode(&val, in.text, in.len, &offset,
APR_JSON_FLAGS_WHITESPACE, level, pool);
if (APR_SUCCESS == status) {
apr_json_kv_t *kv;
const char *cty = NULL;
/*
* 9. Distinguishing between JWS and JWE Objects
*
* If the object is using the JWS JSON Serialization or the JWE JSON
* Serialization, the members used will be different. JWSs have a
* "payload" member and JWEs do not. JWEs have a "ciphertext" member
* and JWSs do not.
*/
/* are we JWS? */
kv = apr_json_object_get(val, APR_JOSE_JWS_PAYLOAD,
APR_JSON_VALUE_STRING);
if (kv) {
return apr_jose_decode_json_jws(jose, val, typ, cty,
kv->v, cb, level, flags, pool, bb);
}
/* are we JWE? */
kv = apr_json_object_get(val, APR_JOSE_JWE_CIPHERTEXT,
APR_JSON_VALUE_STRING);
if (kv) {
return apr_jose_decode_json_jwe(jose, val, typ, cty,
kv->v, cb, level, flags, pool, bb);
}
apr_errprintf(&(*jose)->result, pool, NULL, 0,
"Syntax error: JOSE JSON contained neither a 'payload' nor a 'ciphertext'");
return APR_EINVAL;
}
else {
char buf[1024];
apr_strerror(status, buf, sizeof(buf));
apr_errprintf(&(*jose)->result, pool, NULL, 0,
"Syntax error: JOSE JSON decoding failed at character %" APR_OFF_T_FMT ": %s",
offset, buf);
}
return status;
}
APR_DECLARE(apr_status_t) apr_jose_decode(apr_jose_t **jose, const char *typ,
apr_bucket_brigade *brigade,
apr_jose_cb_t *cb, int level,
int flags, apr_pool_t *pool)
{
/* handle JOSE and JOSE+JSON */
if (typ) {
switch (typ[0]) {
case 'a':
case 'A': {
if (!strncasecmp(typ, "application/", 12)) {
const char *sub = typ + 12;
if (!strcasecmp(sub, "jwt")) {
return apr_jose_decode_compact(jose, typ, brigade, cb,
level, flags, pool);
} else if (!strcasecmp(sub, "jose")) {
return apr_jose_decode_compact(jose, NULL, brigade, cb,
level, flags, pool);
} else if (!strcasecmp(sub, "jose+json")) {
return apr_jose_decode_json(jose, NULL, brigade, cb, level,
flags, pool);
} else if (!strcasecmp(sub, "jwk+json")) {
return apr_jose_decode_jwk(jose, typ, brigade, cb, level,
flags, pool);
} else if (!strcasecmp(sub, "jwk-set+json")) {
return apr_jose_decode_jwks(jose, typ, brigade, cb, level,
flags, pool);
}
}
break;
}
case 'J':
case 'j': {
if (!strcasecmp(typ, "JWT")) {
return apr_jose_decode_compact(jose, typ, brigade, cb, level, flags,
pool);
} else if (!strcasecmp(typ, "JOSE")) {
return apr_jose_decode_compact(jose, NULL, brigade, cb, level,
flags, pool);
} else if (!strcasecmp(typ, "JOSE+JSON")) {
return apr_jose_decode_json(jose, NULL, brigade, cb, level, flags,
pool);
} else if (!strcasecmp(typ, "JWK+JSON")) {
return apr_jose_decode_jwk(jose, typ, brigade, cb, level, flags,
pool);
} else if (!strcasecmp(typ, "JWK-SET+JSON")) {
return apr_jose_decode_jwks(jose, typ, brigade, cb, level, flags,
pool);
}
break;
}
}
}
return apr_jose_decode_data(jose, typ, brigade, cb, level, flags, pool);
}