Coverage Report

Created: 2026-08-31 07:13

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/mod_auth_openidc/src/jose.c
Line
Count
Source
1
/*
2
 * Licensed to the Apache Software Foundation (ASF) under one
3
 * or more contributor license agreements.  See the NOTICE file
4
 * distributed with this work for additional information
5
 * regarding copyright ownership.  The ASF licenses this file
6
 * to you under the Apache License, Version 2.0 (the
7
 * "License"); you may not use this file except in compliance
8
 * with the License.  You may obtain a copy of the License at
9
 *
10
 *   http://www.apache.org/licenses/LICENSE-2.0
11
 *
12
 * Unless required by applicable law or agreed to in writing,
13
 * software distributed under the License is distributed on an
14
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15
 * KIND, either express or implied.  See the License for the
16
 * specific language governing permissions and limitations
17
 * under the License.
18
 */
19
20
/***************************************************************************
21
 * Copyright (C) 2017-2026 ZmartZone Holding BV
22
 * Copyright (C) 2013-2017 Ping Identity Corporation
23
 * All rights reserved.
24
 *
25
 * DISCLAIMER OF WARRANTIES:
26
 *
27
 * THE SOFTWARE PROVIDED HEREUNDER IS PROVIDED ON AN "AS IS" BASIS, WITHOUT
28
 * ANY WARRANTIES OR REPRESENTATIONS EXPRESS, IMPLIED OR STATUTORY; INCLUDING,
29
 * WITHOUT LIMITATION, WARRANTIES OF QUALITY, PERFORMANCE, NONINFRINGEMENT,
30
 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.  NOR ARE THERE ANY
31
 * WARRANTIES CREATED BY A COURSE OR DEALING, COURSE OF PERFORMANCE OR TRADE
32
 * USAGE.  FURTHERMORE, THERE ARE NO WARRANTIES THAT THE SOFTWARE WILL MEET
33
 * YOUR NEEDS OR BE FREE FROM ERRORS, OR THAT THE OPERATION OF THE SOFTWARE
34
 * WILL BE UNINTERRUPTED.  IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
35
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
36
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES HOWEVER CAUSED AND ON ANY THEORY OF
37
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
38
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
39
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
40
 *
41
 * JSON Web Token handling
42
 *
43
 * @Author: Hans Zandbelt - hans.zandbelt@openidc.com
44
 */
45
46
#include <apr_base64.h>
47
#define APR_WANT_BYTEFUNC
48
#include <apr_want.h>
49
50
#ifdef USE_LIBBROTLI
51
#include <brotli/decode.h>
52
#include <brotli/encode.h>
53
#endif
54
#ifdef USE_ZLIB
55
#include <zlib.h>
56
#endif
57
58
#include "jose.h"
59
60
/* cjose exposes the backend JSON type, so this file is the only raw JSON user outside json.c. */
61
#include <jansson.h>
62
63
#include <cjose/cjose.h>
64
65
#include <openssl/bn.h>
66
#include <openssl/err.h>
67
#include <openssl/evp.h>
68
#include <openssl/hmac.h>
69
#include <openssl/opensslv.h>
70
#include <openssl/pem.h>
71
#include <openssl/rsa.h>
72
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
73
#include <openssl/core_names.h>
74
#endif
75
76
#include "util/util.h"
77
78
#include "jose/internal.h"
79
80
/*
81
 * assemble an error report
82
 */
83
void _oidc_jose_error_set(oidc_jose_error_t *error, const char *source, const int line, const char *function,
84
0
        const char *fmt, ...) {
85
0
  if (error == NULL)
86
0
    return;
87
0
  snprintf(error->source, OIDC_JOSE_ERROR_SOURCE_LENGTH, "%s", source);
88
0
  error->line = line;
89
0
  snprintf(error->function, OIDC_JOSE_ERROR_FUNCTION_LENGTH, "%s", function);
90
0
  va_list ap;
91
0
  va_start(ap, fmt);
92
0
  vsnprintf(error->text, OIDC_JOSE_ERROR_TEXT_LENGTH, fmt ? fmt : "(null)", ap);
93
0
  va_end(ap);
94
0
}
95
96
/*
97
 * hash a sequence of bytes with a specific algorithm and return the result as a base64url-encoded \0 terminated string
98
 */
99
apr_byte_t oidc_jose_hash_and_base64url_encode(apr_pool_t *pool, const char *openssl_hash_algo, const char *input,
100
0
                 int input_len, char **output, oidc_jose_error_t *err) {
101
0
  unsigned char *hashed = NULL;
102
0
  unsigned int hashed_len = 0;
103
0
  if (oidc_jose_hash_bytes(pool, openssl_hash_algo, (const unsigned char *)input, input_len, &hashed, &hashed_len,
104
0
         err) == FALSE)
105
0
    return FALSE;
106
0
  char *out = NULL;
107
0
  size_t out_len;
108
0
  cjose_err cjose_err;
109
0
  if (cjose_base64url_encode(hashed, hashed_len, &out, &out_len, &cjose_err) == FALSE) {
110
0
    oidc_jose_error(err, "cjose_base64url_encode failed: %s", oidc_cjose_e2s(pool, cjose_err));
111
0
    return FALSE;
112
0
  }
113
0
  *output = apr_pstrmemdup(pool, out, out_len);
114
0
  cjose_get_dealloc()(out);
115
0
  return TRUE;
116
0
}
117
118
/*
119
 * check if a string is an element of an array of strings
120
 */
121
0
static apr_byte_t oidc_jose_array_has_string(apr_array_header_t *haystack, const char *needle) {
122
0
  int i = 0;
123
0
  while (i < haystack->nelts) {
124
0
    if (_oidc_strcmp(APR_ARRAY_IDX(haystack, i, const char *), needle) == 0)
125
0
      return TRUE;
126
0
    i++;
127
0
  }
128
0
  return FALSE;
129
0
}
130
131
/*
132
 * return all supported signing algorithms
133
 */
134
0
apr_array_header_t *oidc_jose_jws_supported_algorithms(apr_pool_t *pool) {
135
0
  apr_array_header_t *result = apr_array_make(pool, 12, sizeof(const char *));
136
0
  APR_ARRAY_PUSH(result, const char *) = CJOSE_HDR_ALG_RS256;
137
0
  APR_ARRAY_PUSH(result, const char *) = CJOSE_HDR_ALG_RS384;
138
0
  APR_ARRAY_PUSH(result, const char *) = CJOSE_HDR_ALG_RS512;
139
0
  APR_ARRAY_PUSH(result, const char *) = CJOSE_HDR_ALG_PS256;
140
0
  APR_ARRAY_PUSH(result, const char *) = CJOSE_HDR_ALG_PS384;
141
0
  APR_ARRAY_PUSH(result, const char *) = CJOSE_HDR_ALG_PS512;
142
0
  APR_ARRAY_PUSH(result, const char *) = CJOSE_HDR_ALG_HS256;
143
0
  APR_ARRAY_PUSH(result, const char *) = CJOSE_HDR_ALG_HS384;
144
0
  APR_ARRAY_PUSH(result, const char *) = CJOSE_HDR_ALG_HS512;
145
0
#if (OIDC_JOSE_EC_SUPPORT)
146
0
  APR_ARRAY_PUSH(result, const char *) = CJOSE_HDR_ALG_ES256;
147
0
  APR_ARRAY_PUSH(result, const char *) = CJOSE_HDR_ALG_ES384;
148
0
  APR_ARRAY_PUSH(result, const char *) = CJOSE_HDR_ALG_ES512;
149
0
#endif
150
0
  APR_ARRAY_PUSH(result, const char *) = CJOSE_HDR_ALG_NONE;
151
0
  return result;
152
0
}
153
154
/*
155
 * check if the provided signing algorithm is supported
156
 */
157
0
apr_byte_t oidc_jose_jws_algorithm_is_supported(apr_pool_t *pool, const char *alg) {
158
0
  return oidc_jose_array_has_string(oidc_jose_jws_supported_algorithms(pool), alg);
159
0
}
160
161
/*
162
 * return all supported content encryption key algorithms
163
 */
164
0
apr_array_header_t *oidc_jose_jwe_supported_algorithms(apr_pool_t *pool) {
165
0
  apr_array_header_t *result = apr_array_make(pool, 4, sizeof(const char *));
166
0
  APR_ARRAY_PUSH(result, const char *) = CJOSE_HDR_ALG_A128KW;
167
0
  APR_ARRAY_PUSH(result, const char *) = CJOSE_HDR_ALG_A192KW;
168
0
  APR_ARRAY_PUSH(result, const char *) = CJOSE_HDR_ALG_A256KW;
169
0
  APR_ARRAY_PUSH(result, const char *) = CJOSE_HDR_ALG_RSA_OAEP;
170
0
  return result;
171
0
}
172
173
/*
174
 * check if the provided content encryption key algorithm is supported
175
 */
176
0
apr_byte_t oidc_jose_jwe_algorithm_is_supported(apr_pool_t *pool, const char *alg) {
177
0
  return oidc_jose_array_has_string(oidc_jose_jwe_supported_algorithms(pool), alg);
178
0
}
179
180
/*
181
 * return all supported encryption algorithms
182
 */
183
0
apr_array_header_t *oidc_jose_jwe_supported_encryptions(apr_pool_t *pool) {
184
0
  apr_array_header_t *result = apr_array_make(pool, 5, sizeof(const char *));
185
0
  APR_ARRAY_PUSH(result, const char *) = CJOSE_HDR_ENC_A128CBC_HS256;
186
0
  APR_ARRAY_PUSH(result, const char *) = CJOSE_HDR_ENC_A192CBC_HS384;
187
0
  APR_ARRAY_PUSH(result, const char *) = CJOSE_HDR_ENC_A256CBC_HS512;
188
0
#if (OIDC_JOSE_GCM_SUPPORT)
189
0
  APR_ARRAY_PUSH(result, const char *) = CJOSE_HDR_ENC_A256GCM;
190
0
#endif
191
0
  return result;
192
0
}
193
194
/*
195
 * check if the provided encryption algorithm is supported
196
 */
197
0
apr_byte_t oidc_jose_jwe_encryption_is_supported(apr_pool_t *pool, const char *enc) {
198
0
  return oidc_jose_array_has_string(oidc_jose_jwe_supported_encryptions(pool), enc);
199
0
}
200
201
/*
202
 * get (optional) string from JWT
203
 */
204
apr_byte_t oidc_jose_get_string(apr_pool_t *pool, const json_t *json, const char *claim_name, apr_byte_t is_mandatory,
205
0
        char **result, oidc_jose_error_t *err) {
206
0
  const json_t *v = json_object_get(json, claim_name);
207
0
  if (v != NULL) {
208
0
    if (json_is_string(v)) {
209
0
      *result = apr_pstrdup(pool, json_string_value(v));
210
0
    } else if (is_mandatory) {
211
0
      oidc_jose_error(err, "mandatory JSON key \"%s\" was found but the type is not a string",
212
0
          claim_name);
213
0
      return FALSE;
214
0
    }
215
0
  } else if (is_mandatory) {
216
0
    oidc_jose_error(err, "mandatory JSON key \"%s\" could not be found", claim_name);
217
0
    return FALSE;
218
0
  }
219
0
  return TRUE;
220
0
}
221
222
/*
223
 * parse (optional) timestamp from payload
224
 */
225
apr_byte_t oidc_jose_get_timestamp(const json_t *json, const char *claim_name, apr_byte_t is_mandatory, double *result,
226
0
           oidc_jose_error_t *err) {
227
0
  *result = OIDC_JWT_CLAIM_TIME_EMPTY;
228
0
  const json_t *v = json_object_get(json, claim_name);
229
0
  if (v != NULL) {
230
0
    if (json_is_number(v)) {
231
0
      *result = json_number_value(v);
232
0
    } else if (is_mandatory) {
233
0
      oidc_jose_error(err, "mandatory JSON key \"%s\" was found but the type is not a number",
234
0
          claim_name);
235
0
      return FALSE;
236
0
    }
237
0
  } else if (is_mandatory) {
238
0
    oidc_jose_error(err, "mandatory JSON key \"%s\" could not be found", claim_name);
239
0
    return FALSE;
240
0
  }
241
0
  return TRUE;
242
0
}
243
244
0
#define OIDC_CJOSE_UNCOMPRESS_CHUNK 8192
245
/* absolute cap on the decompressed output to prevent decompression bombs */
246
0
#define OIDC_CJOSE_UNCOMPRESS_MAX (10 * 1024 * 1024)
247
248
#ifdef USE_LIBBROTLI
249
250
/*
251
 * deflate using libbrotli
252
 */
253
static apr_byte_t oidc_jose_brotli_compress(apr_pool_t *pool, const char *input, int input_len, char **output,
254
              int *output_len, oidc_jose_error_t *err) {
255
  size_t len = BrotliEncoderMaxCompressedSize(input_len);
256
  *output = apr_pcalloc(pool, len);
257
  if (BrotliEncoderCompress(BROTLI_DEFAULT_QUALITY, BROTLI_DEFAULT_WINDOW, BROTLI_MODE_TEXT, input_len,
258
          (const uint8_t *)input, &len, (uint8_t *)*output) != BROTLI_TRUE) {
259
    oidc_jose_error(err, "BrotliEncoderCompress failed: compression error or buffer too small");
260
    return FALSE;
261
  }
262
  *output_len = len;
263
  return TRUE;
264
}
265
266
/* Stream Brotli output into a doubling buffer so valid high-ratio payloads fit below the bomb cap. */
267
static apr_byte_t oidc_jose_brotli_uncompress(apr_pool_t *pool, const char *input, int input_len, char **output,
268
                int *output_len, oidc_jose_error_t *err) {
269
  apr_byte_t rv = FALSE;
270
  BrotliDecoderResult res = BROTLI_DECODER_RESULT_ERROR;
271
  size_t len = OIDC_CJOSE_UNCOMPRESS_CHUNK;
272
  size_t available_in = (size_t)input_len;
273
  const uint8_t *next_in = (const uint8_t *)input;
274
  size_t total_out = 0;
275
  char *buf = apr_pcalloc(pool, len);
276
  char *tmp = NULL;
277
278
  BrotliDecoderState *state = BrotliDecoderCreateInstance(NULL, NULL, NULL);
279
  if (state == NULL) {
280
    oidc_jose_error(err, "BrotliDecoderCreateInstance failed");
281
    return FALSE;
282
  }
283
284
  while (1) {
285
    size_t available_out = len - total_out;
286
    uint8_t *next_out = (uint8_t *)(buf + total_out);
287
    res = BrotliDecoderDecompressStream(state, &available_in, &next_in, &available_out, &next_out, NULL);
288
    total_out = len - available_out;
289
    if (res == BROTLI_DECODER_RESULT_SUCCESS)
290
      break;
291
    if (res != BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT) {
292
      /* a decode error, or a truncated stream asking for more input */
293
      oidc_jose_error(err, "BrotliDecoderDecompressStream failed: %d", res);
294
      goto end;
295
    }
296
    if (len >= OIDC_CJOSE_UNCOMPRESS_MAX) {
297
      oidc_jose_error(err, "brotli output would exceed %d bytes", OIDC_CJOSE_UNCOMPRESS_MAX);
298
      goto end;
299
    }
300
    /* grow by doubling, as the zlib side does and for the same reasons */
301
    size_t next = (len > OIDC_CJOSE_UNCOMPRESS_MAX / 2) ? OIDC_CJOSE_UNCOMPRESS_MAX : len * 2;
302
    tmp = apr_pcalloc(pool, next);
303
    _oidc_memcpy(tmp, buf, len);
304
    len = next;
305
    buf = tmp;
306
  }
307
308
  *output = buf;
309
  *output_len = (int)total_out;
310
  rv = TRUE;
311
312
end:
313
314
  BrotliDecoderDestroyInstance(state);
315
316
  return rv;
317
}
318
319
#endif
320
321
#ifdef USE_ZLIB
322
323
/*
324
 * deflate using zlib
325
 */
326
static apr_byte_t oidc_jose_zlib_compress(apr_pool_t *pool, const char *input, int input_len, char **output,
327
0
            int *output_len, oidc_jose_error_t *err) {
328
0
  apr_byte_t rv = FALSE;
329
0
  int status = Z_OK;
330
0
  z_stream zlib;
331
332
0
  zlib.zalloc = Z_NULL;
333
0
  zlib.zfree = Z_NULL;
334
0
  zlib.opaque = Z_NULL;
335
0
  zlib.next_in = (Bytef *)input;
336
0
  zlib.avail_in = input_len;
337
338
  /*
339
   * Small window and memory settings avoid oversized allocations for short payloads. The zlib
340
   * header records the window size, so the default inflate path remains compatible.
341
   */
342
0
  status = deflateInit2(&zlib, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 12, 5, Z_DEFAULT_STRATEGY);
343
0
  if (status != Z_OK) {
344
0
    oidc_jose_error(err, "deflateInit2() failed: %d", status);
345
0
    goto end;
346
0
  }
347
348
  /* deflateBound accounts for the zlib header/trailer overhead that dominates for
349
   * short inputs; a fixed input_len * 2 buffer made deflate fail on values of a
350
   * few bytes, silently preventing such values from being cached */
351
  /* the (uInt) narrowing is safe: input_len is an int so deflateBound's small
352
   * relative overhead keeps the result well within uInt range */
353
0
  uLong output_max = deflateBound(&zlib, input_len);
354
0
  *output = apr_pcalloc(pool, output_max);
355
0
  zlib.next_out = (Bytef *)(*output);
356
0
  zlib.avail_out = (uInt)output_max;
357
358
0
  status = deflate(&zlib, Z_FINISH);
359
0
  if (status != Z_STREAM_END) {
360
0
    oidc_jose_error(err, "deflate() failed: %d", status);
361
0
    goto end;
362
0
  }
363
364
0
  *output_len = (int)zlib.total_out;
365
366
0
  rv = TRUE;
367
368
0
end:
369
370
0
  deflateEnd(&zlib);
371
372
0
  return rv;
373
0
}
374
375
/*
376
 * inflate using zlib
377
 */
378
static apr_byte_t oidc_jose_zlib_uncompress(apr_pool_t *pool, const char *input, int input_len, char **output,
379
0
              int *output_len, apr_byte_t *capped, oidc_jose_error_t *err) {
380
0
  apr_byte_t rv = FALSE;
381
0
  int status = Z_OK;
382
0
  size_t len = OIDC_CJOSE_UNCOMPRESS_CHUNK;
383
0
  char *tmp = NULL;
384
0
  char *buf = apr_pcalloc(pool, len);
385
0
  z_stream zlib;
386
387
  /* whether inflation was stopped at the output cap: the one failure that must not be passed
388
   * through as "not a zlib stream after all" - see the caller */
389
0
  *capped = FALSE;
390
391
0
  zlib.zalloc = Z_NULL;
392
0
  zlib.zfree = Z_NULL;
393
0
  zlib.opaque = Z_NULL;
394
0
  zlib.avail_in = (uInt)input_len;
395
0
  zlib.next_in = (Bytef *)input;
396
0
  zlib.total_out = 0;
397
398
0
  status = inflateInit(&zlib);
399
0
  if (status != Z_OK) {
400
0
    oidc_jose_error(err, "inflateInit() failed: %d", status);
401
0
    goto end;
402
0
  }
403
404
0
  while (status == Z_OK) {
405
    /* Double pooled buffers; fixed growth retains old allocations and becomes quadratic. */
406
0
    if (zlib.total_out >= len) {
407
0
      if (len >= OIDC_CJOSE_UNCOMPRESS_MAX) {
408
0
        *capped = TRUE;
409
0
        oidc_jose_error(err, "inflate() output would exceed %d bytes",
410
0
            OIDC_CJOSE_UNCOMPRESS_MAX);
411
0
        goto end;
412
0
      }
413
0
      size_t next = (len > OIDC_CJOSE_UNCOMPRESS_MAX / 2) ? OIDC_CJOSE_UNCOMPRESS_MAX : len * 2;
414
0
      tmp = apr_pcalloc(pool, next);
415
0
      _oidc_memcpy(tmp, buf, len);
416
0
      len = next;
417
0
      buf = tmp;
418
0
    }
419
0
    zlib.next_out = (Bytef *)(buf + zlib.total_out);
420
0
    zlib.avail_out = (uInt)(len - zlib.total_out);
421
0
    status = inflate(&zlib, Z_SYNC_FLUSH);
422
0
  }
423
424
0
  if (status != Z_STREAM_END) {
425
0
    oidc_jose_error(err, "inflate() failed: %d", status);
426
0
    goto end;
427
0
  }
428
429
0
  *output_len = (int)zlib.total_out;
430
0
  *output = buf;
431
432
0
  rv = TRUE;
433
434
0
end:
435
436
0
  inflateEnd(&zlib);
437
438
0
  return rv;
439
0
}
440
441
#endif
442
443
/*
444
 * compress using (compile-time) zlib or libbrotli, otherwise just plain copy
445
 */
446
apr_byte_t oidc_jose_compress(apr_pool_t *pool, const char *input, int input_len, char **output, int *output_len,
447
0
            oidc_jose_error_t *err) {
448
#ifdef USE_LIBBROTLI
449
  return oidc_jose_brotli_compress(pool, input, input_len, output, output_len, err);
450
#elif defined(USE_ZLIB)
451
  return oidc_jose_zlib_compress(pool, input, input_len, output, output_len, err);
452
#else
453
  *output = apr_pmemdup(pool, input, input_len);
454
  *output_len = input_len;
455
  return TRUE;
456
#endif
457
0
}
458
459
/* Recognize a possible zlib stream from its RFC 1950 header; callers handle false positives. */
460
0
static apr_byte_t oidc_jose_is_zlib(const char *input, int input_len) {
461
0
  const unsigned char *b = (const unsigned char *)input;
462
0
  if ((input == NULL) || (input_len < 2))
463
0
    return FALSE;
464
0
  if ((b[0] & 0x0f) != 8)
465
0
    return FALSE;
466
0
  return (((((unsigned int)b[0]) << 8) | b[1]) % 31) == 0 ? TRUE : FALSE;
467
0
}
468
469
/*
470
 * Detect compression from the payload so caches remain readable across build or configuration
471
 * changes. A build without zlib still cannot decode a zlib payload.
472
 */
473
apr_byte_t oidc_jose_uncompress(apr_pool_t *pool, const char *input, int input_len, char **output, int *output_len,
474
0
        oidc_jose_error_t *err) {
475
476
0
  if (oidc_jose_is_zlib(input, input_len)) {
477
0
#ifdef USE_ZLIB
478
    /*
479
     * The zlib header test has false positives for arbitrary cache values. Treat inflate
480
     * errors as uncompressed input; only the decompression-size cap is a hard failure.
481
     */
482
0
    apr_byte_t capped = FALSE;
483
0
    if (oidc_jose_zlib_uncompress(pool, input, input_len, output, output_len, &capped, err) == TRUE)
484
0
      return TRUE;
485
0
    if (capped == TRUE)
486
0
      return FALSE;
487
0
#endif
488
    /* Without zlib support, pass possible zlib input through for downstream validation. */
489
0
  }
490
491
#ifdef USE_LIBBROTLI
492
  /* Brotli has no header; a failed decode may simply mean the payload is uncompressed. */
493
  if (oidc_jose_brotli_uncompress(pool, input, input_len, output, output_len, err) == TRUE)
494
    return TRUE;
495
#endif
496
  /* not compressed, or compressed by an algorithm this build does not have */
497
0
  *output = apr_pmemdup(pool, input, input_len);
498
0
  *output_len = input_len;
499
0
  return TRUE;
500
0
}
501
502
#if (OPENSSL_VERSION_NUMBER < 0x10100000) || defined(LIBRESSL_VERSION_NUMBER)
503
EVP_MD_CTX *EVP_MD_CTX_new() {
504
  return malloc(sizeof(EVP_MD_CTX));
505
}
506
void EVP_MD_CTX_free(EVP_MD_CTX *ctx) {
507
  if (ctx)
508
    free(ctx);
509
}
510
#endif
511
512
0
#define OIDC_JOSE_CJOSE_VERSION_DEPRECATED "0.4."
513
514
/*
515
 * return the version string of the underlying JOSE backend library
516
 */
517
0
const char *oidc_jose_version(void) {
518
0
  return cjose_version();
519
0
}
520
521
/*
522
 * check for a version of cjose < 0.5.0 that has a version of
523
 * cjose_jws_verify that resources after a verification failure
524
 */
525
0
apr_byte_t oidc_jose_version_deprecated(apr_pool_t *pool) {
526
0
  const char *version = apr_pstrdup(pool, cjose_version());
527
0
  return (_oidc_strstr(version, OIDC_JOSE_CJOSE_VERSION_DEPRECATED) == version);
528
0
}
529
530
/*
531
 * hash a byte sequence with the specified algorithm
532
 */
533
apr_byte_t oidc_jose_hash_bytes(apr_pool_t *pool, const char *s_digest, const unsigned char *input,
534
        unsigned int input_len, unsigned char **output, unsigned int *output_len,
535
3.26k
        oidc_jose_error_t *err) {
536
3.26k
  apr_byte_t rv = FALSE;
537
3.26k
  unsigned char md_value[EVP_MAX_MD_SIZE];
538
3.26k
  const EVP_MD *evp_digest = NULL;
539
3.26k
  EVP_MD_CTX *ctx = EVP_MD_CTX_new();
540
3.26k
  EVP_MD_CTX_init(ctx);
541
542
3.26k
  if ((evp_digest = EVP_get_digestbyname(s_digest)) == NULL) {
543
0
    oidc_jose_error(err, "no OpenSSL digest algorithm found for algorithm \"%s\"", s_digest);
544
0
    goto end;
545
0
  }
546
547
3.26k
  if (!EVP_DigestInit_ex(ctx, evp_digest, NULL)) {
548
0
    oidc_jose_error_openssl(err, "EVP_DigestInit_ex");
549
0
    goto end;
550
0
  }
551
552
3.26k
  if (!EVP_DigestUpdate(ctx, input, input_len)) {
553
0
    oidc_jose_error_openssl(err, "EVP_DigestUpdate");
554
0
    goto end;
555
0
  }
556
557
3.26k
  if (!EVP_DigestFinal(ctx, md_value, output_len)) {
558
0
    oidc_jose_error_openssl(err, "EVP_DigestFinal");
559
0
    goto end;
560
0
  }
561
562
3.26k
  *output = apr_pmemdup(pool, md_value, *output_len);
563
564
3.26k
  rv = TRUE;
565
566
3.26k
end:
567
568
3.26k
  if (ctx)
569
3.26k
    EVP_MD_CTX_free(ctx);
570
571
3.26k
  return rv;
572
3.26k
}
573
574
/* Map JOSE algorithms to digest name and length together; cjose names are not constant expressions. */
575
0
static apr_byte_t _oidc_jose_alg_to_sha2(const char *alg, const char **openssl_digest, int *hash_len) {
576
0
  const struct {
577
0
    const char *algs[4];
578
0
    const char *openssl_digest;
579
0
    int hash_len;
580
0
  } families[] = {
581
0
      {{CJOSE_HDR_ALG_RS256, CJOSE_HDR_ALG_PS256, CJOSE_HDR_ALG_HS256, CJOSE_HDR_ALG_ES256}, LN_sha256, 32},
582
0
      {{CJOSE_HDR_ALG_RS384, CJOSE_HDR_ALG_PS384, CJOSE_HDR_ALG_HS384, CJOSE_HDR_ALG_ES384}, LN_sha384, 48},
583
0
      {{CJOSE_HDR_ALG_RS512, CJOSE_HDR_ALG_PS512, CJOSE_HDR_ALG_HS512, CJOSE_HDR_ALG_ES512}, LN_sha512, 64},
584
0
  };
585
0
  for (int i = 0; i < (int)(sizeof(families) / sizeof(families[0])); i++) {
586
0
    for (int j = 0; j < (int)(sizeof(families[0].algs) / sizeof(const char *)); j++) {
587
0
      if (_oidc_strcmp(alg, families[i].algs[j]) == 0) {
588
0
        *openssl_digest = families[i].openssl_digest;
589
0
        *hash_len = families[i].hash_len;
590
0
        return TRUE;
591
0
      }
592
0
    }
593
0
  }
594
0
  return FALSE;
595
0
}
596
597
/*
598
 * return the OpenSSL hash algorithm associated with a specified JWT algorithm
599
 */
600
0
static const char *oidc_jose_alg_to_openssl_digest(const char *alg) {
601
0
  const char *digest = NULL;
602
0
  int len = 0;
603
0
  return _oidc_jose_alg_to_sha2(alg, &digest, &len) ? digest : NULL;
604
0
}
605
606
/*
607
 * hash a string value with the specified algorithm
608
 */
609
apr_byte_t oidc_jose_hash_string(apr_pool_t *pool, const char *alg, const char *msg, char **hash,
610
0
         unsigned int *hash_len, oidc_jose_error_t *err) {
611
612
0
  const char *s_digest = oidc_jose_alg_to_openssl_digest(alg);
613
0
  if (s_digest == NULL) {
614
0
    oidc_jose_error(err, "no OpenSSL digest algorithm name found for algorithm \"%s\"", alg);
615
0
    return FALSE;
616
0
  }
617
618
0
  return oidc_jose_hash_bytes(pool, s_digest, (const unsigned char *)msg, (unsigned int)_oidc_strlen(msg),
619
0
            (unsigned char **)hash, hash_len, err);
620
0
}
621
622
/*
623
 * return hash length for the specified JOSE algorithm
624
 */
625
0
int oidc_jose_hash_length(const char *alg) {
626
0
  const char *digest = NULL;
627
0
  int len = 0;
628
0
  return _oidc_jose_alg_to_sha2(alg, &digest, &len) ? len : 0;
629
0
}