blob: 220c6f2b8cd64f6d1632fcce1a018e339381eea6 [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
* 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;
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);
apr_json_object_set(json, "protected",
APR_JSON_VALUE_STRING,
apr_json_string_create(p, buf, len), p);
}
/* create unprotected header */
if (e->unprotected) {
apr_json_object_set(json, "unprotected",
APR_JSON_VALUE_STRING, 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 */
apr_json_object_set(json, "header",
APR_JSON_VALUE_STRING, 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);
apr_json_object_set(json, "encrypted_key",
APR_JSON_VALUE_STRING,
apr_json_string_create(p, buf64, len64), p);
}
/* create recipients */
if (jwe->recipients) {
apr_json_value_t *recips;
int i;
/* create recipients element */
recips = apr_json_array_create(p, jwe->recipients->nelts);
apr_json_object_set(json, "recipients",
APR_JSON_VALUE_STRING, 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 */
apr_json_object_set(r, "header",
APR_JSON_VALUE_STRING, 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);
apr_json_object_set(r, "encrypted_key",
APR_JSON_VALUE_STRING,
apr_json_string_create(p, buf64, len64), 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);
apr_json_object_set(json, "iv", APR_JSON_VALUE_STRING,
apr_json_string_create(p, buf64, len64), p);
}
/* create aad */
if (e->aad.len) {
buf64 = apr_pencode_base64_binary(p, e->aad.data, e->aad.len,
APR_ENCODE_BASE64URL, &len64);
apr_json_object_set(json, "aad", APR_JSON_VALUE_STRING,
apr_json_string_create(p, buf64, len64), p);
}
/* create ciphertext */
if (e->cipher.len) {
buf64 = apr_pencode_base64_binary(p, e->cipher.data, e->cipher.len,
APR_ENCODE_BASE64URL, &len64);
apr_json_object_set(json, "ciphertext", APR_JSON_VALUE_STRING,
apr_json_string_create(p, buf64, len64), p);
}
/* create tag */
if (e->tag.len) {
buf64 = apr_pencode_base64_binary(p, e->tag.data, e->tag.len,
APR_ENCODE_BASE64URL, &len64);
apr_json_object_set(json, "tag", APR_JSON_VALUE_STRING,
apr_json_string_create(p, buf64, len64), 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;
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 */
apr_json_object_set(json, "payload", APR_JSON_VALUE_STRING,
apr_json_string_create(p, buf64, len64), 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;
}
apr_json_object_set(json, "protected", APR_JSON_VALUE_STRING,
apr_json_string_create(p, buf, len), 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 */
apr_json_object_set(json, "header", APR_JSON_VALUE_STRING,
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);
apr_json_object_set(json, "signature", APR_JSON_VALUE_STRING,
apr_json_string_create(p, buf64, len64), p);
}
/* otherwise calculate the general signatures */
else if (jws->signatures) {
apr_json_value_t *sigs;
int i;
/* create signatures element */
sigs = apr_json_array_create(p, jws->signatures->nelts);
apr_json_object_set(json, "signatures", APR_JSON_VALUE_STRING,
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 */
apr_json_object_set(s, "protected", APR_JSON_VALUE_STRING,
apr_json_string_create(p, buf, len), 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 */
apr_json_object_set(s, "header", APR_JSON_VALUE_STRING,
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);
apr_json_object_set(s, "signature", APR_JSON_VALUE_STRING,
apr_json_string_create(p, buf64, len64), 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;
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;
}