summaryrefslogtreecommitdiff
path: root/jose
diff options
context:
space:
mode:
authorGraham Leggett <minfrin@apache.org>2018-09-01 11:17:14 +0000
committerGraham Leggett <minfrin@apache.org>2018-09-01 11:17:14 +0000
commit05673b3dde8b8d3e7262e88015bee0efb46adee4 (patch)
tree5b55d0f34051c7ddeef9959e66dab5a8fb0dda96 /jose
parentf0cf7790b9594f068ba0879bb08bf29a44b1b5e7 (diff)
downloadapr-05673b3dde8b8d3e7262e88015bee0efb46adee4.tar.gz
apr_jose: Add support for encoding and decoding of JSON Object
Signing and Encryption messages as per RFC7515, RFC7516, RFC7517 and RFC7519. git-svn-id: https://svn.apache.org/repos/asf/apr/apr/trunk@1839819 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'jose')
-rw-r--r--jose/apr_jose.c353
-rw-r--r--jose/apr_jose_decode.c1694
-rw-r--r--jose/apr_jose_encode.c825
3 files changed, 2872 insertions, 0 deletions
diff --git a/jose/apr_jose.c b/jose/apr_jose.c
new file mode 100644
index 000000000..26f6b0ffa
--- /dev/null
+++ b/jose/apr_jose.c
@@ -0,0 +1,353 @@
+/* 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
+ * limitations under the License.
+ */
+
+#include "apr_jose.h"
+
+APR_DECLARE(apu_err_t *) apr_jose_error(apr_jose_t *jose)
+{
+ return &jose->result;
+}
+
+APR_DECLARE(apr_status_t) apr_jose_make(apr_jose_t **jose, apr_jose_type_e type,
+ apr_pool_t *pool)
+{
+ apr_jose_t *j;
+
+ if (*jose) {
+ j = *jose;
+ } else {
+ *jose = j = apr_pcalloc(pool, sizeof(apr_jose_t));
+ if (!j) {
+ return APR_ENOMEM;
+ }
+ }
+
+ j->pool = pool;
+ j->type = type;
+
+ return APR_SUCCESS;
+}
+
+APR_DECLARE(apr_status_t) apr_jose_data_make(apr_jose_t **jose, const char *typ,
+ const unsigned char *in, apr_size_t inlen, apr_pool_t *pool)
+{
+ apr_jose_t *j;
+ apr_status_t status;
+
+ status = apr_jose_make(jose, APR_JOSE_TYPE_DATA, pool);
+ if (APR_SUCCESS != status) {
+ return status;
+ }
+ j = *jose;
+
+ j->typ = typ;
+ j->jose.data = apr_palloc(pool, sizeof(apr_jose_data_t));
+ if (!j->jose.data) {
+ return APR_ENOMEM;
+ }
+ j->jose.data->data = in;
+ j->jose.data->len = inlen;
+
+ return APR_SUCCESS;
+}
+
+APR_DECLARE(apr_status_t) apr_jose_json_make(apr_jose_t **jose, const char *cty,
+ apr_json_value_t *json, apr_pool_t *pool)
+{
+ apr_jose_t *j;
+ apr_status_t status;
+
+ status = apr_jose_make(jose, APR_JOSE_TYPE_JSON, pool);
+ if (APR_SUCCESS != status) {
+ return status;
+ }
+ j = *jose;
+
+ j->cty = cty;
+ j->jose.json = apr_palloc(pool, sizeof(apr_jose_json_t));
+ if (!j->jose.json) {
+ return APR_ENOMEM;
+ }
+ j->jose.json->json = json;
+
+ return APR_SUCCESS;
+}
+
+APR_DECLARE(apr_status_t) apr_jose_signature_make(
+ apr_jose_signature_t **signature, apr_json_value_t *header,
+ apr_json_value_t *protected, apr_pool_t *pool)
+{
+ apr_jose_signature_t *s;
+
+ *signature = s = apr_pcalloc(pool, sizeof(apr_jose_signature_t));
+ if (!s) {
+ return APR_ENOMEM;
+ }
+
+ s->header = header;
+ s->protected_header = protected;
+
+ return APR_SUCCESS;
+}
+
+APR_DECLARE(apr_status_t) apr_jose_recipient_make(
+ apr_jose_recipient_t **recipient, apr_json_value_t *header,
+ apr_pool_t *pool)
+{
+ apr_jose_recipient_t *r;
+
+ *recipient = r = apr_pcalloc(pool, sizeof(apr_jose_recipient_t));
+ if (!r) {
+ return APR_ENOMEM;
+ }
+
+ r->header = header;
+
+ return APR_SUCCESS;
+}
+
+APR_DECLARE(apr_status_t) apr_jose_encryption_make(
+ apr_jose_encryption_t **encryption, apr_json_value_t *header,
+ apr_json_value_t *protected_header, apr_pool_t *pool)
+{
+ apr_jose_encryption_t *e;
+
+ *encryption = e = apr_pcalloc(pool, sizeof(apr_jose_encryption_t));
+ if (!e) {
+ return APR_ENOMEM;
+ }
+
+ e->unprotected = header;
+ e->protected = protected_header;
+
+ return APR_SUCCESS;
+}
+
+APR_DECLARE(apr_status_t) apr_jose_jwe_make(apr_jose_t **jose,
+ apr_jose_recipient_t *recipient, apr_array_header_t *recipients,
+ apr_jose_encryption_t *encryption, apr_jose_t *payload,
+ apr_pool_t *pool)
+{
+ apr_jose_t *j;
+ apr_jose_jwe_t *jwe;
+ apr_status_t status;
+
+ status = apr_jose_make(jose, APR_JOSE_TYPE_JWE, pool);
+ if (APR_SUCCESS != status) {
+ return status;
+ }
+ j = *jose;
+
+ j->cty = payload->cty;
+
+ jwe = j->jose.jwe = apr_palloc(pool, sizeof(apr_jose_jwe_t));
+ if (!jwe) {
+ return APR_ENOMEM;
+ }
+
+ jwe->recipient = recipient;
+ jwe->recipients = recipients;
+ jwe->encryption = encryption;
+ jwe->payload = payload;
+
+ return APR_SUCCESS;
+}
+
+APR_DECLARE(apr_status_t) apr_jose_jwe_json_make(apr_jose_t **jose,
+ apr_jose_recipient_t *recipient, apr_array_header_t *recipients,
+ apr_jose_encryption_t *encryption, apr_jose_t *payload,
+ apr_pool_t *pool)
+{
+ apr_jose_t *j;
+ apr_jose_jwe_t *jwe;
+ apr_status_t status;
+
+ status = apr_jose_make(jose, APR_JOSE_TYPE_JWE_JSON, pool);
+ if (APR_SUCCESS != status) {
+ return status;
+ }
+ j = *jose;
+
+ if (payload) {
+ j->cty = payload->cty;
+ }
+
+ jwe = j->jose.jwe = apr_palloc(pool, sizeof(apr_jose_jwe_t));
+ if (!jwe) {
+ return APR_ENOMEM;
+ }
+
+ jwe->recipient = recipient;
+ jwe->recipients = recipients;
+ jwe->encryption = encryption;
+ jwe->payload = payload;
+
+ return APR_SUCCESS;
+}
+
+APR_DECLARE(apr_status_t) apr_jose_jwk_make(apr_jose_t **jose,
+ apr_json_value_t *key, apr_pool_t *pool)
+{
+ apr_jose_t *j;
+ apr_jose_jwk_t *jwk;
+ apr_status_t status;
+
+ status = apr_jose_make(jose, APR_JOSE_TYPE_JWK, pool);
+ if (APR_SUCCESS != status) {
+ return status;
+ }
+ j = *jose;
+
+ jwk = j->jose.jwk = apr_palloc(pool, sizeof(apr_jose_jwk_t));
+ if (!jwk) {
+ return APR_ENOMEM;
+ }
+
+ jwk->key = key;
+
+ return APR_SUCCESS;
+}
+
+APR_DECLARE(apr_status_t) apr_jose_jwks_make(apr_jose_t **jose,
+ apr_json_value_t *keys, apr_pool_t *pool)
+{
+ apr_jose_t *j;
+ apr_jose_jwks_t *jwks;
+ apr_status_t status;
+
+ status = apr_jose_make(jose, APR_JOSE_TYPE_JWKS, pool);
+ if (APR_SUCCESS != status) {
+ return status;
+ }
+ j = *jose;
+
+ jwks = j->jose.jwks = apr_palloc(pool, sizeof(apr_jose_jwks_t));
+ if (!jwks) {
+ return APR_ENOMEM;
+ }
+
+ jwks->keys = keys;
+
+ return APR_SUCCESS;
+}
+
+APR_DECLARE(apr_status_t) apr_jose_jws_make(apr_jose_t **jose,
+ apr_jose_signature_t *signature, apr_array_header_t *signatures,
+ apr_jose_t *payload, apr_pool_t *pool)
+{
+ apr_jose_t *j;
+ apr_jose_jws_t *jws;
+ apr_status_t status;
+
+ status = apr_jose_make(jose, APR_JOSE_TYPE_JWS, pool);
+ if (APR_SUCCESS != status) {
+ return status;
+ }
+ j = *jose;
+
+ if (payload) {
+ j->cty = payload->cty;
+ }
+
+ jws = j->jose.jws = apr_pcalloc(pool, sizeof(apr_jose_jws_t));
+ if (!jws) {
+ return APR_ENOMEM;
+ }
+
+ jws->signature = signature;
+ jws->signatures = signatures;
+ jws->payload = payload;
+
+ return APR_SUCCESS;
+}
+
+APR_DECLARE(apr_status_t) apr_jose_jws_json_make(apr_jose_t **jose,
+ apr_jose_signature_t *signature, apr_array_header_t *signatures,
+ apr_jose_t *payload, apr_pool_t *pool)
+{
+ apr_jose_t *j;
+ apr_jose_jws_t *jws;
+ apr_status_t status;
+
+ status = apr_jose_make(jose, APR_JOSE_TYPE_JWS_JSON, pool);
+ if (APR_SUCCESS != status) {
+ return status;
+ }
+ j = *jose;
+
+ if (payload) {
+ j->cty = payload->cty;
+ }
+
+ jws = j->jose.jws = apr_pcalloc(pool, sizeof(apr_jose_jws_t));
+ if (!jws) {
+ return APR_ENOMEM;
+ }
+
+ jws->signature = signature;
+ jws->signatures = signatures;
+ jws->payload = payload;
+
+ return APR_SUCCESS;
+}
+
+APR_DECLARE(apr_status_t) apr_jose_jwt_make(apr_jose_t **jose, apr_json_value_t *claims,
+ apr_pool_t *pool)
+{
+ apr_jose_t *j;
+ apr_jose_jwt_t *jwt;
+ apr_status_t status;
+
+ status = apr_jose_make(jose, APR_JOSE_TYPE_JWT, pool);
+ if (APR_SUCCESS != status) {
+ return status;
+ }
+ j = *jose;
+
+ j->cty = "JWT";
+
+ jwt = j->jose.jwt = apr_palloc(pool, sizeof(apr_jose_jwt_t));
+ if (!jwt) {
+ return APR_ENOMEM;
+ }
+
+ jwt->claims = claims;
+
+ return APR_SUCCESS;
+}
+
+APR_DECLARE(apr_status_t) apr_jose_text_make(apr_jose_t **jose, const char *cty,
+ const char *in, apr_size_t inlen, apr_pool_t *pool)
+{
+ apr_jose_t *j;
+ apr_status_t status;
+
+ status = apr_jose_make(jose, APR_JOSE_TYPE_TEXT, pool);
+ if (APR_SUCCESS != status) {
+ return status;
+ }
+ j = *jose;
+
+ j->cty = cty;
+ j->jose.text = apr_palloc(pool, sizeof(apr_jose_text_t));
+ if (!j->jose.text) {
+ return APR_ENOMEM;
+ }
+ j->jose.text->text = in;
+ j->jose.text->len = inlen;
+
+ return APR_SUCCESS;
+}
diff --git a/jose/apr_jose_decode.c b/jose/apr_jose_decode.c
new file mode 100644
index 000000000..7d0b01d49
--- /dev/null
+++ b/jose/apr_jose_decode.c
@@ -0,0 +1,1694 @@
+/* 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"
+
+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);
+}
+
+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_jose_text_t in;
+ apr_off_t offset;
+ apr_status_t status;
+
+ status = apr_jose_jwk_make(jose, NULL, pool);
+ if (APR_SUCCESS != status) {
+ return status;
+ }
+
+ status = apr_jose_flatten(bb, &in, pool);
+ if (APR_SUCCESS != status) {
+ return status;
+ }
+
+ status = apr_json_decode(&(*jose)->jose.jwk->key, in.text, in.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: JWK decoding failed at offset %" APR_OFF_T_FMT ": %s",
+ offset, buf);
+
+ return status;
+ }
+
+ return APR_SUCCESS;
+}
+
+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_jose_text_t in;
+ apr_off_t offset;
+ apr_status_t status;
+
+ status = apr_jose_jwks_make(jose, NULL, pool);
+ if (APR_SUCCESS != status) {
+ return status;
+ }
+
+ status = apr_jose_flatten(bb, &in, pool);
+ if (APR_SUCCESS != status) {
+ return status;
+ }
+
+ status = apr_json_decode(&(*jose)->jose.jwks->keys, in.text, in.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: JWKS decoding failed at offset %" APR_OFF_T_FMT ": %s",
+ offset, buf);
+
+ return status;
+ }
+
+ if ((*jose)->jose.jwks->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;
+}
+
+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_jose_text_t in;
+ apr_off_t offset;
+ apr_status_t status;
+
+ status = apr_jose_jwt_make(jose, NULL, pool);
+ if (APR_SUCCESS != status) {
+ return status;
+ }
+
+ status = apr_jose_flatten(bb, &in, pool);
+ if (APR_SUCCESS != status) {
+ return status;
+ }
+
+ status = apr_json_decode(&(*jose)->jose.jwt->claims, in.text, in.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: JWT decoding failed at offset %" APR_OFF_T_FMT ": %s",
+ offset, buf);
+
+ return status;
+ }
+
+ return APR_SUCCESS;
+}
+
+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;
+ }
+
+ status = apr_jose_data_make(jose, typ, (const unsigned char *) in.text, in.len,
+ pool);
+ if (APR_SUCCESS != status) {
+ return status;
+ }
+
+ return status;
+}
+
+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;
+}
+
+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;
+}
+
+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;
+ }
+
+ status = apr_jose_jws_make(jose, NULL, NULL, NULL, pool);
+ if (APR_SUCCESS != status) {
+ return status;
+ }
+ jws = (*jose)->jose.jws;
+
+ /*
+ * If using the JWS Compact Serialization, let the JOSE Header be
+ * the JWS Protected Header.
+ */
+
+ status = apr_jose_signature_make(&jws->signature, NULL, ph, pool);
+ if (APR_SUCCESS != status) {
+ return status;
+ }
+
+ 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;
+}
+
+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;
+ }
+
+ status = apr_jose_jwe_make(jose, NULL, NULL, NULL, NULL, pool);
+ if (APR_SUCCESS != status) {
+ return status;
+ }
+ jwe = (*jose)->jose.jwe;
+
+ status = apr_jose_encryption_make(&jwe->encryption, NULL,
+ NULL, pool);
+ if (APR_SUCCESS != status) {
+ return status;
+ }
+
+ status = apr_jose_recipient_make(&jwe->recipient, NULL, pool);
+ if (APR_SUCCESS != status) {
+ return status;
+ }
+
+ /*
+ * 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;
+}
+
+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;
+
+ status = apr_jose_make(jose, APR_JOSE_TYPE_NONE, pool);
+ if (APR_SUCCESS != status) {
+ return status;
+ }
+
+ 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 {
+
+ status = apr_jose_decode(
+ flags & APR_JOSE_FLAG_DECODE_ALL ?
+ &(*jose)->jose.jws->payload : jose, typ, bb, cb,
+ level, flags, pool);
+ }
+
+ }
+
+ return status;
+}
+
+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;
+ }
+
+ status = apr_jose_jws_json_make(jose, NULL, NULL, NULL, pool);
+ if (APR_SUCCESS != status) {
+ return status;
+ }
+ 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);
+
+ 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;
+ }
+
+ status = apr_jose_signature_make(&jws->signature, NULL, NULL,
+ pool);
+ if (APR_SUCCESS != status) {
+ return status;
+ }
+
+ 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);
+
+ return apr_jose_decode(
+ flags & APR_JOSE_FLAG_DECODE_ALL ?
+ &(*jose)->jose.jws->payload : jose, typ, bb, cb,
+ level, flags, pool);
+}
+
+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;
+ }
+
+ status = apr_jose_jwe_json_make(jose, NULL, NULL, NULL, NULL, pool);
+ if (APR_SUCCESS != status) {
+ return status;
+ }
+ jwe = (*jose)->jose.jwe;
+
+ status = apr_jose_encryption_make(&jwe->encryption, NULL,
+ NULL, pool);
+ if (APR_SUCCESS != status) {
+ return status;
+ }
+
+ /*
+ * 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) {
+
+ 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) {
+
+ 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;
+
+}
+
+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;
+
+ status = apr_jose_make(jose, APR_JOSE_TYPE_NONE, pool);
+ if (APR_SUCCESS != status) {
+ return status;
+ }
+
+ 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_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);
+}
diff --git a/jose/apr_jose_encode.c b/jose/apr_jose_encode.c
new file mode 100644
index 000000000..43a4adc8b
--- /dev/null
+++ b/jose/apr_jose_encode.c
@@ -0,0 +1,825 @@
+/* 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
+ * limitations under the License.
+ */
+
+#include "apr_jose.h"
+#include "apr_encode.h"
+
+static apr_status_t apr_jose_encode_base64_json(apr_bucket_brigade *brigade,
+ apr_brigade_flush flush, void *ctx, apr_json_value_t *json,
+ apr_pool_t *pool)
+{
+ apr_status_t status = APR_SUCCESS;
+
+ if (json) {
+
+ apr_bucket_brigade *bb;
+
+ bb = apr_brigade_create(pool, brigade->bucket_alloc);
+
+ status = apr_json_encode(bb, flush, ctx, json,
+ APR_JSON_FLAGS_WHITESPACE, pool);
+ if (APR_SUCCESS == status) {
+
+ char *buf;
+ apr_size_t buflen;
+ const char *buf64;
+ apr_size_t buf64len;
+
+ apr_brigade_pflatten(bb, &buf, &buflen, pool);
+ buf64 = apr_pencode_base64(pool, buf, buflen, APR_ENCODE_BASE64URL,
+ &buf64len);
+ status = apr_brigade_write(brigade, flush, ctx, buf64, buf64len);
+
+ }
+
+ }
+
+ return status;
+}
+
+static apr_status_t apr_jose_encode_compact_jwe(apr_bucket_brigade *brigade,
+ apr_brigade_flush flush, void *ctx, apr_jose_t *jose, apr_jose_cb_t *cb,
+ apr_pool_t *p)
+{
+ apr_bucket_brigade *bb = apr_brigade_create(p,
+ brigade->bucket_alloc);
+
+ apr_jose_jwe_t *jwe = jose->jose.jwe;
+
+ apr_status_t status = APR_SUCCESS;
+
+ /*
+ * 7.1. JWE Compact Serialization
+ *
+ * The JWE Compact Serialization represents encrypted content as a
+ * compact, URL-safe string. This string is:
+ *
+ * BASE64URL(UTF8(JWE Protected Header)) || '.' ||
+ */
+
+ status = apr_jose_encode_base64_json(brigade, flush, ctx,
+ jwe->encryption->protected, p);
+ if (APR_SUCCESS != status) {
+ return status;
+ }
+
+ status = apr_brigade_write(brigade, flush, ctx, ".", 1);
+ if (APR_SUCCESS != status) {
+ return status;
+ }
+
+ if (cb && cb->encrypt) {
+ status = apr_jose_encode(bb, flush, ctx, jwe->payload, cb, p);
+ if (APR_SUCCESS != status) {
+ jose->result = jwe->payload->result;
+ return status;
+ }
+ status = cb->encrypt(bb, jose, jwe->recipient, jwe->encryption,
+ cb->ctx, p);
+ if (APR_SUCCESS != status) {
+ return status;
+ }
+ }
+
+ if (APR_SUCCESS == status) {
+
+ struct iovec vec[7];
+
+ /*
+ * 7. Compute the encoded key value BASE64URL(JWE Encrypted Key).
+ *
+ * 10. Compute the encoded Initialization Vector value BASE64URL(JWE
+ * Initialization Vector).
+ *
+ * 16. Compute the encoded ciphertext value BASE64URL(JWE Ciphertext).
+ *
+ * 17. Compute the encoded Authentication Tag value BASE64URL(JWE
+ * Authentication Tag).
+ *
+ * 18. If a JWE AAD value is present, compute the encoded AAD value
+ * BASE64URL(JWE AAD).
+ *
+ * 19. Create the desired serialized output. The Compact Serialization
+ * of this result is the string BASE64URL(UTF8(JWE Protected
+ * Header)) || '.' || BASE64URL(JWE Encrypted Key) || '.' ||
+ * BASE64URL(JWE Initialization Vector) || '.' || BASE64URL(JWE
+ * Ciphertext) || '.' || BASE64URL(JWE Authentication Tag). The
+ * JWE JSON Serialization is described in Section 7.2.
+ */
+
+ /*
+ * BASE64URL(JWE Encrypted Key) || '.' ||
+ */
+
+ vec[0].iov_base = (void *) apr_pencode_base64_binary(p,
+ jwe->recipient->ekey.data, jwe->recipient->ekey.len,
+ APR_ENCODE_BASE64URL, &vec[0].iov_len);
+ vec[1].iov_base = ".";
+ vec[1].iov_len = 1;
+
+ /*
+ * BASE64URL(JWE Initialization Vector) || '.' ||
+ */
+
+ vec[2].iov_base = (void *) apr_pencode_base64_binary(p,
+ jwe->encryption->iv.data, jwe->encryption->iv.len,
+ APR_ENCODE_BASE64URL, &vec[2].iov_len);
+ vec[3].iov_base = ".";
+ vec[3].iov_len = 1;
+
+ /*
+ * BASE64URL(JWE Ciphertext) || '.' ||
+ */
+
+ vec[4].iov_base = (void *) apr_pencode_base64_binary(p,
+ jwe->encryption->cipher.data, jwe->encryption->cipher.len,
+ APR_ENCODE_BASE64URL, &vec[4].iov_len);
+ vec[5].iov_base = ".";
+ vec[5].iov_len = 1;
+
+ /*
+ * BASE64URL(JWE Authentication Tag)
+ */
+
+ vec[6].iov_base = (void *)apr_pencode_base64_binary(p, jwe->encryption->tag.data, jwe->encryption->tag.len,
+ APR_ENCODE_BASE64URL, &vec[6].iov_len);
+
+ status = apr_brigade_writev(brigade, flush, ctx, vec, 7);
+
+ }
+
+ return status;
+}
+
+static apr_status_t apr_jose_encode_compact_jws(apr_bucket_brigade *brigade,
+ apr_brigade_flush flush, void *ctx, apr_jose_t *jose, apr_jose_cb_t *cb,
+ apr_pool_t *p)
+{
+ apr_bucket *e;
+ apr_bucket_brigade *bb = apr_brigade_create(p,
+ brigade->bucket_alloc);
+ apr_jose_text_t payload;
+ apr_jose_text_t payload64;
+
+ apr_jose_jws_t *jws = jose->jose.jws;
+
+ apr_status_t status;
+
+ status = apr_jose_encode(bb, flush, ctx, jws->payload, cb, p);
+ if (APR_SUCCESS != status) {
+ jose->result = jws->payload->result;
+ return status;
+ }
+
+ status = apr_brigade_pflatten(bb, (char **)&payload.text, &payload.len, p);
+ if (APR_SUCCESS != status) {
+ return status;
+ }
+
+ payload64.text = apr_pencode_base64(p, payload.text, payload.len,
+ APR_ENCODE_BASE64URL, &payload64.len);
+
+ apr_brigade_cleanup(bb);
+
+ /*
+ * 7.1. JWS Compact Serialization
+ *
+ * The JWS Compact Serialization represents digitally signed or MACed
+ * content as a compact, URL-safe string. This string is:
+ *
+ * BASE64URL(UTF8(JWS Protected Header)) || '.' ||
+ */
+
+ if (jws->signature) {
+ status = apr_jose_encode_base64_json(bb, flush, ctx,
+ jws->signature->protected_header, p);
+ if (APR_SUCCESS != status) {
+ return status;
+ }
+ }
+
+ status = apr_brigade_write(bb, flush, ctx, ".", 1);
+ if (APR_SUCCESS != status) {
+ return status;
+ }
+
+ /*
+ * BASE64URL(JWS Payload) ||
+ */
+
+ e = apr_bucket_pool_create(payload64.text, payload64.len, p,
+ bb->bucket_alloc);
+ APR_BRIGADE_INSERT_TAIL(bb, e);
+
+ /*
+ * '.' || BASE64URL(JWS Signature)
+ */
+
+ if (cb && cb->sign && jws->signature) {
+ status = cb->sign(bb, jose, jws->signature, cb->ctx, p);
+ if (APR_SUCCESS != status) {
+ return status;
+ }
+ }
+
+ APR_BRIGADE_CONCAT(brigade, bb);
+
+ status = apr_brigade_write(brigade, flush, ctx, ".", 1);
+ if (APR_SUCCESS != status) {
+ return status;
+ }
+
+ if (jws->signature && jws->signature->sig.data && APR_SUCCESS == status) {
+
+ const char *buf64;
+ apr_size_t buf64len;
+
+ buf64 = apr_pencode_base64_binary(p, jws->signature->sig.data,
+ jws->signature->sig.len,
+ APR_ENCODE_BASE64URL, &buf64len);
+ status = apr_brigade_write(brigade, flush, ctx, buf64,
+ buf64len);
+
+ }
+
+ return status;
+}
+
+static apr_status_t apr_jose_encode_json_jwe(apr_bucket_brigade *brigade,
+ apr_brigade_flush flush, void *ctx, apr_jose_t *jose, apr_jose_cb_t *cb,
+ apr_pool_t *p)
+{
+
+ apr_json_value_t *json, *key, *val;
+ char *buf;
+ const char *buf64;
+ apr_size_t len;
+ apr_size_t len64;
+
+ apr_bucket_brigade *bb = apr_brigade_create(p,
+ brigade->bucket_alloc);
+
+ apr_jose_jwe_t *jwe = jose->jose.jwe;
+
+ apr_status_t status = APR_SUCCESS;
+
+ /* create our json */
+
+ json = apr_json_object_create(p);
+
+ /* create protected header */
+
+ if (jwe->encryption) {
+ apr_jose_encryption_t *e = jwe->encryption;
+
+ if (e->protected) {
+
+ status = apr_jose_encode_base64_json(bb, flush, ctx,
+ e->protected, p);
+ if (APR_SUCCESS != status) {
+ return status;
+ }
+
+ status = apr_brigade_pflatten(bb, &buf, &len, p);
+ if (APR_SUCCESS != status) {
+ return status;
+ }
+
+ apr_brigade_cleanup(bb);
+
+ key = apr_json_string_create(p, "protected",
+ APR_JSON_VALUE_STRING);
+ val = apr_json_string_create(p, buf, len);
+ apr_json_object_set(json, key, val, p);
+
+ }
+
+ /* create unprotected header */
+
+ if (e->unprotected) {
+
+ key = apr_json_string_create(p, "unprotected",
+ APR_JSON_VALUE_STRING);
+ apr_json_object_set(json, key, e->unprotected, p);
+
+ }
+
+ /* create recipient */
+ if (jwe->recipient) {
+
+ apr_jose_recipient_t *recip = jwe->recipient;
+
+ /* create the payload */
+
+ status = apr_jose_encode(bb, flush, ctx, jwe->payload, cb, p);
+ if (APR_SUCCESS != status) {
+ jose->result = jwe->payload->result;
+ return status;
+ }
+
+ if (cb && cb->encrypt && recip) {
+ status = cb->encrypt(bb, jose, recip, e, cb->ctx, p);
+ if (APR_SUCCESS != status) {
+ return status;
+ }
+ }
+
+ apr_brigade_cleanup(bb);
+
+ /* create header */
+
+ key = apr_json_string_create(p, "header",
+ APR_JSON_VALUE_STRING);
+ apr_json_object_set(json, key, recip->header, p);
+
+ apr_brigade_cleanup(bb);
+
+ /* create encrypted key */
+
+ buf64 = apr_pencode_base64_binary(p, recip->ekey.data,
+ recip->ekey.len,
+ APR_ENCODE_BASE64URL, &len64);
+
+ key = apr_json_string_create(p, "encrypted_key",
+ APR_JSON_VALUE_STRING);
+ val = apr_json_string_create(p, buf64, len64);
+ apr_json_object_set(json, key, val, p);
+
+ }
+
+ /* create recipients */
+ if (jwe->recipients) {
+
+ apr_json_value_t *recips;
+ int i;
+
+ /* create recipients element */
+
+ key = apr_json_string_create(p, "recipients",
+ APR_JSON_VALUE_STRING);
+ recips = apr_json_array_create(p, jwe->recipients->nelts);
+ apr_json_object_set(json, key, recips, p);
+
+ /* populate each recipient */
+
+ for (i = 0; i < jwe->recipients->nelts; i++) {
+ apr_json_value_t *r = apr_json_object_create(p);
+ apr_jose_recipient_t *recip = APR_ARRAY_IDX(
+ jwe->recipients, i, apr_jose_recipient_t *);
+
+ if (!recip) {
+ continue;
+ }
+
+ apr_json_array_add(recips, r);
+
+ /* create the payload */
+
+ status = apr_jose_encode(bb, flush, ctx, jwe->payload, cb, p);
+ if (APR_SUCCESS != status) {
+ jose->result = jwe->payload->result;
+ return status;
+ }
+
+ if (cb && cb->encrypt && recip) {
+ status = cb->encrypt(bb, jose, recip, e, cb->ctx, p);
+ if (APR_SUCCESS != status) {
+ return status;
+ }
+ }
+
+ apr_brigade_cleanup(bb);
+
+ /* create header */
+
+ key = apr_json_string_create(p, "header",
+ APR_JSON_VALUE_STRING);
+ apr_json_object_set(r, key, recip->header, p);
+
+ apr_brigade_cleanup(bb);
+
+ /* create encrypted key */
+
+ buf64 = apr_pencode_base64_binary(p, recip->ekey.data,
+ recip->ekey.len,
+ APR_ENCODE_BASE64URL, &len64);
+
+ key = apr_json_string_create(p, "encrypted_key",
+ APR_JSON_VALUE_STRING);
+ val = apr_json_string_create(p, buf64, len64);
+ apr_json_object_set(r, key, val, p);
+
+ }
+ if (APR_SUCCESS != status) {
+ return status;
+ }
+
+ }
+
+ /* create iv */
+
+ if (e->iv.len) {
+
+ buf64 = apr_pencode_base64_binary(p, e->iv.data, e->iv.len,
+ APR_ENCODE_BASE64URL, &len64);
+
+ key = apr_json_string_create(p, "iv", APR_JSON_VALUE_STRING);
+ val = apr_json_string_create(p, buf64, len64);
+ apr_json_object_set(json, key, val, p);
+ }
+
+ /* create aad */
+
+ if (e->aad.len) {
+
+ buf64 = apr_pencode_base64_binary(p, e->aad.data, e->aad.len,
+ APR_ENCODE_BASE64URL, &len64);
+
+ key = apr_json_string_create(p, "aad", APR_JSON_VALUE_STRING);
+ val = apr_json_string_create(p, buf64, len64);
+ apr_json_object_set(json, key, val, p);
+ }
+
+ /* create ciphertext */
+
+ if (e->cipher.len) {
+
+ buf64 = apr_pencode_base64_binary(p, e->cipher.data, e->cipher.len,
+ APR_ENCODE_BASE64URL, &len64);
+
+ key = apr_json_string_create(p, "ciphertext", APR_JSON_VALUE_STRING);
+ val = apr_json_string_create(p, buf64, len64);
+ apr_json_object_set(json, key, val, p);
+ }
+
+ /* create tag */
+
+ if (e->tag.len) {
+
+ buf64 = apr_pencode_base64_binary(p, e->tag.data, e->tag.len,
+ APR_ENCODE_BASE64URL, &len64);
+
+ key = apr_json_string_create(p, "tag", APR_JSON_VALUE_STRING);
+ val = apr_json_string_create(p, buf64, len64);
+ apr_json_object_set(json, key, val, p);
+ }
+
+ }
+
+ /* write out our final result */
+
+ if (json) {
+ status = apr_json_encode(brigade, flush, ctx, json,
+ APR_JSON_FLAGS_WHITESPACE, p);
+ }
+
+ return status;
+}
+
+static apr_status_t apr_jose_encode_json_jws(apr_bucket_brigade *brigade,
+ apr_brigade_flush flush, void *ctx, apr_jose_t *jose, apr_jose_cb_t *cb,
+ apr_pool_t *p)
+{
+ apr_json_value_t *json, *key, *val;
+ char *buf;
+ const char *buf64;
+ apr_size_t len;
+ apr_size_t len64;
+
+ apr_bucket_brigade *bb = apr_brigade_create(p,
+ brigade->bucket_alloc);
+
+ apr_jose_jws_t *jws = jose->jose.jws;
+
+ apr_status_t status = APR_SUCCESS;
+
+ /* create our json */
+
+ json = apr_json_object_create(p);
+
+ /* calculate BASE64URL(JWS Payload) */
+
+ status = apr_jose_encode(bb, flush, ctx, jws->payload, cb, p);
+ if (APR_SUCCESS != status) {
+ jose->result = jws->payload->result;
+ return status;
+ }
+
+ status = apr_brigade_pflatten(bb, &buf, &len, p);
+ if (APR_SUCCESS != status) {
+ return status;
+ }
+
+ buf64 = apr_pencode_base64(p, buf, len,
+ APR_ENCODE_BASE64URL, &len64);
+
+ apr_brigade_cleanup(bb);
+
+ /* add the payload to our json */
+
+ key = apr_json_string_create(p, "payload", APR_JSON_VALUE_STRING);
+ val = apr_json_string_create(p, buf64, len64);
+ apr_json_object_set(json, key, val, p);
+
+ /* calculate the flattened signature */
+
+ if (jws->signature) {
+
+ /* create protected header */
+
+ status = apr_jose_encode_base64_json(bb, flush, ctx,
+ jws->signature->protected_header, p);
+ if (APR_SUCCESS != status) {
+ return status;
+ }
+
+ status = apr_brigade_pflatten(bb, &buf, &len, p);
+ if (APR_SUCCESS != status) {
+ return status;
+ }
+
+ key = apr_json_string_create(p, "protected", APR_JSON_VALUE_STRING);
+ val = apr_json_string_create(p, buf, len);
+ apr_json_object_set(json, key, val, p);
+
+ status = apr_brigade_write(bb, flush, ctx, ".", 1);
+ if (APR_SUCCESS != status) {
+ return status;
+ }
+
+ status = apr_brigade_write(bb, flush, ctx, buf64, len64);
+ if (APR_SUCCESS != status) {
+ return status;
+ }
+
+ if (cb && cb->sign && jws->signature) {
+ status = cb->sign(bb, jose, jws->signature, cb->ctx, p);
+ if (APR_SUCCESS != status) {
+ return status;
+ }
+ }
+
+ apr_brigade_cleanup(bb);
+
+ /* create header */
+
+ key = apr_json_string_create(p, "header", APR_JSON_VALUE_STRING);
+ apr_json_object_set(json, key, jws->signature->header, p);
+
+ apr_brigade_cleanup(bb);
+
+ /* create signature */
+
+ buf64 = apr_pencode_base64_binary(p,
+ jws->signature->sig.data, jws->signature->sig.len,
+ APR_ENCODE_BASE64URL, &len64);
+
+ key = apr_json_string_create(p, "signature", APR_JSON_VALUE_STRING);
+ val = apr_json_string_create(p, buf64, len64);
+ apr_json_object_set(json, key, val, p);
+
+ }
+
+ /* otherwise calculate the general signatures */
+
+ else if (jws->signatures) {
+
+ apr_json_value_t *sigs;
+ int i;
+
+ /* create signatures element */
+
+ key = apr_json_string_create(p, "signatures", APR_JSON_VALUE_STRING);
+ sigs = apr_json_array_create(p, jws->signatures->nelts);
+ apr_json_object_set(json, key, sigs, p);
+
+ /* populate each signature */
+
+ for (i = 0; i < jws->signatures->nelts; i++) {
+ apr_json_value_t *s = apr_json_object_create(p);
+ apr_jose_signature_t *sig = APR_ARRAY_IDX(
+ jws->signatures, i, apr_jose_signature_t *);
+
+ apr_json_array_add(sigs, s);
+
+ /* create protected header */
+
+ status = apr_jose_encode_base64_json(bb, flush, ctx,
+ sig->protected_header, p);
+ if (APR_SUCCESS != status) {
+ return status;
+ }
+
+ status = apr_brigade_pflatten(bb, &buf, &len, p);
+ if (APR_SUCCESS != status) {
+ return status;
+ }
+
+ /* add protected header to array */
+
+ key = apr_json_string_create(p, "protected", APR_JSON_VALUE_STRING);
+ val = apr_json_string_create(p, buf, len);
+ apr_json_object_set(s, key, val, p);
+
+ status = apr_brigade_write(bb, flush, ctx, ".", 1);
+ if (APR_SUCCESS != status) {
+ return status;
+ }
+
+ status = apr_brigade_write(bb, flush, ctx, buf64, len64);
+ if (APR_SUCCESS != status) {
+ return status;
+ }
+
+ if (cb && cb->sign && sig) {
+ status = cb->sign(bb, jose, sig, cb->ctx, p);
+ if (APR_SUCCESS != status) {
+ return status;
+ }
+ }
+
+ apr_brigade_cleanup(bb);
+
+ /* create header */
+
+ key = apr_json_string_create(p, "header", APR_JSON_VALUE_STRING);
+ apr_json_object_set(s, key, sig->header, p);
+
+ apr_brigade_cleanup(bb);
+
+ /* create signature */
+
+ buf64 = apr_pencode_base64_binary(p, sig->sig.data,
+ sig->sig.len,
+ APR_ENCODE_BASE64URL, &len64);
+
+ key = apr_json_string_create(p, "signature", APR_JSON_VALUE_STRING);
+ val = apr_json_string_create(p, buf64, len64);
+ apr_json_object_set(s, key, val, p);
+
+ }
+ if (APR_SUCCESS != status) {
+ return status;
+ }
+
+ }
+
+ /* write out our final result */
+
+ if (json) {
+ status = apr_json_encode(brigade, flush, ctx, json,
+ APR_JSON_FLAGS_WHITESPACE, p);
+ }
+
+ return status;
+}
+
+apr_status_t apr_jose_encode(apr_bucket_brigade *brigade,
+ apr_brigade_flush flush, void *ctx, apr_jose_t *jose, apr_jose_cb_t *cb,
+ apr_pool_t *pool)
+{
+ apr_pool_t *p;
+ apr_status_t status = APR_EINVAL;
+
+ /* if asked to encode nothing, encode nothing */
+ if (jose == NULL) {
+ return APR_SUCCESS;
+ }
+
+ apr_pool_create(&p, pool);
+ if (p == NULL) {
+ return APR_ENOMEM;
+ }
+
+ /* first, generic data types */
+ switch (jose->type) {
+ case APR_JOSE_TYPE_JWK: {
+
+ /* do nothing for now */
+
+ break;
+ }
+
+ case APR_JOSE_TYPE_JWKS: {
+
+ /* do nothing for now */
+
+ break;
+ }
+
+ case APR_JOSE_TYPE_DATA: {
+
+ apr_jose_data_t *data = jose->jose.data;
+
+ if (data) {
+
+ struct iovec vec[1];
+
+ vec[0].iov_base = (void *)data->data;
+ vec[0].iov_len = data->len;
+
+ status = apr_brigade_writev(brigade, flush, ctx, vec, 1);
+ if (APR_SUCCESS != status) {
+ break;
+ }
+ }
+
+ break;
+ }
+
+ case APR_JOSE_TYPE_TEXT: {
+
+ apr_jose_text_t *text = jose->jose.text;
+
+ if (text) {
+ status = apr_brigade_write(brigade, flush, ctx, text->text,
+ text->len);
+ if (APR_SUCCESS != status) {
+ break;
+ }
+ }
+
+ break;
+ }
+
+ case APR_JOSE_TYPE_JSON: {
+
+ apr_json_value_t *json = jose->jose.json->json;
+
+ if (json) {
+ status = apr_json_encode(brigade, flush, ctx, json,
+ APR_JSON_FLAGS_WHITESPACE, p);
+ }
+
+ break;
+ }
+
+ case APR_JOSE_TYPE_JWE: {
+
+ status = apr_jose_encode_compact_jwe(brigade, flush, ctx, jose, cb,
+ p);
+
+ break;
+ }
+
+ case APR_JOSE_TYPE_JWE_JSON: {
+
+ status = apr_jose_encode_json_jwe(brigade, flush, ctx, jose, cb, p);
+
+ break;
+ }
+
+ case APR_JOSE_TYPE_JWS: {
+
+ status = apr_jose_encode_compact_jws(brigade, flush, ctx, jose, cb,
+ p);
+
+ break;
+ }
+
+ case APR_JOSE_TYPE_JWS_JSON: {
+
+ status = apr_jose_encode_json_jws(brigade, flush, ctx, jose, cb, p);
+
+ break;
+ }
+
+ case APR_JOSE_TYPE_JWT: {
+
+ apr_json_value_t *claims = jose->jose.jwt->claims;
+
+ if (claims) {
+ status = apr_json_encode(brigade, flush, ctx, claims,
+ APR_JSON_FLAGS_WHITESPACE, p);
+ }
+
+ break;
+ }
+
+ default: {
+ apr_errprintf(&jose->result, pool, NULL, 0,
+ "JOSE type '%d' not recognised", jose->type);
+
+ status = APR_ENOTIMPL;
+
+ break;
+ }
+ }
+
+ apr_pool_destroy(p);
+
+ return status;
+}