Coverage Report

Created: 2026-09-04 06:43

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/mod_auth_openidc/src/jose/jwk.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 Key (JWK) parsing, serialization and PEM/X.509 conversion
42
 *
43
 * @Author: Hans Zandbelt - hans.zandbelt@openidc.com
44
 */
45
46
#include "jose.h"
47
48
#include "jose/internal.h"
49
50
#include <jansson.h>
51
52
#include <cjose/cjose.h>
53
54
#include <openssl/bn.h>
55
#include <openssl/err.h>
56
#include <openssl/evp.h>
57
#include <openssl/opensslv.h>
58
#include <openssl/pem.h>
59
#include <openssl/rsa.h>
60
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
61
#include <openssl/core_names.h>
62
#endif
63
64
#include "util/util.h"
65
66
/*
67
 * extract a b64 encoded certificate representation as a single string
68
 */
69
static int oidc_jose_util_get_b64encoded_certificate_data(apr_pool_t *p, const X509 *x509_cert,
70
19.0k
                char **b64_encoded_certificate, oidc_jose_error_t *err) {
71
19.0k
  int rc = 0;
72
19.0k
  char *name = NULL;
73
19.0k
  char *header = NULL;
74
19.0k
  long len = 0;
75
19.0k
  long b64_len = 0;
76
19.0k
  BIO *bio = NULL;
77
19.0k
  unsigned char *data = NULL;
78
79
19.0k
  if ((bio = BIO_new(BIO_s_mem())) == NULL) {
80
0
    oidc_jose_error_openssl(err, "BIO_new");
81
0
    goto end;
82
0
  }
83
84
19.0k
  if (!PEM_write_bio_X509(bio, (X509 *)x509_cert)) {
85
0
    oidc_jose_error_openssl(err, "PEM_write_bio_X509");
86
0
    goto end;
87
0
  }
88
19.0k
  if (!PEM_read_bio(bio, &name, &header, &data, &len)) {
89
0
    oidc_jose_error_openssl(err, "PEM_read_bio");
90
0
    goto end;
91
0
  }
92
93
  /* "For every 3 bytes of input provided 4 bytes of output data will be produced." */
94
19.0k
  b64_len = (((len + 2) / 3) * 4) + 1;
95
96
19.0k
  *b64_encoded_certificate = (char *)apr_pcalloc(p, b64_len);
97
19.0k
  if (!*b64_encoded_certificate) {
98
0
    oidc_jose_error_openssl(err, "apr_pcalloc");
99
0
    goto end;
100
0
  }
101
102
19.0k
  rc = EVP_EncodeBlock((unsigned char *)*b64_encoded_certificate, data, (int)len);
103
104
19.0k
end:
105
19.0k
  if (bio) {
106
19.0k
    BIO_free(bio);
107
19.0k
  }
108
19.0k
  if (name != NULL) {
109
19.0k
    OPENSSL_free(name);
110
19.0k
  }
111
19.0k
  if (data != NULL) {
112
19.0k
    OPENSSL_free(data);
113
19.0k
  }
114
19.0k
  if (header != NULL) {
115
19.0k
    OPENSSL_free(header);
116
19.0k
  }
117
118
19.0k
  return rc;
119
19.0k
}
120
121
/*
122
 * create a new JWK
123
 */
124
108k
static oidc_jwk_t *oidc_jwk_new(apr_pool_t *pool) {
125
108k
  oidc_jwk_t *jwk = apr_pcalloc(pool, sizeof(oidc_jwk_t));
126
108k
  return jwk;
127
108k
}
128
129
static apr_byte_t _oidc_jwk_parse_x5c(apr_pool_t *pool, const json_t *json, cjose_jwk_t **jwk, oidc_jose_error_t *err);
130
131
27.9k
#define OIDC_JOSE_HDR_KTY "kty"
132
3.97k
#define OIDC_JOSE_HDR_KTY_RSA "RSA"
133
3.24k
#define OIDC_JOSE_HDR_KTY_EC "EC"
134
1.42k
#define OIDC_JOSE_HDR_X5C "x5c"
135
136
/*
137
 * parse a JSON object with an "x5c" JWK representation into a cjose JWK object
138
 */
139
27.9k
static cjose_jwk_t *_oidc_jwk_parse_x5c_spec(apr_pool_t *pool, const json_t *json, oidc_jose_error_t *err) {
140
141
27.9k
  cjose_jwk_t *cjose_jwk = NULL;
142
143
27.9k
  char *kty = NULL;
144
27.9k
  oidc_jose_get_string(pool, json, OIDC_JOSE_HDR_KTY, FALSE, &kty, NULL);
145
27.9k
  if (kty == NULL) {
146
23.9k
    oidc_jose_error(err, "no key type \"" OIDC_JOSE_HDR_KTY "\" found in JWK JSON value");
147
23.9k
    goto end;
148
23.9k
  }
149
150
3.97k
  if ((_oidc_strcmp(kty, OIDC_JOSE_HDR_KTY_RSA) != 0) && (_oidc_strcmp(kty, OIDC_JOSE_HDR_KTY_EC) != 0)) {
151
3.06k
    oidc_jose_error(err, "no \"" OIDC_JOSE_HDR_KTY_RSA "\" or \"" OIDC_JOSE_HDR_KTY_EC
152
3.06k
             "\" key type found JWK JSON value");
153
3.06k
    goto end;
154
3.06k
  }
155
156
909
  const json_t *v = json_object_get(json, OIDC_JOSE_HDR_X5C);
157
909
  if (v == NULL) {
158
397
    oidc_jose_error(err, "no \"" OIDC_JOSE_HDR_X5C "\" key found in JWK JSON value");
159
397
    goto end;
160
397
  }
161
162
512
  _oidc_jwk_parse_x5c(pool, json, &cjose_jwk, err);
163
164
27.9k
end:
165
166
27.9k
  return cjose_jwk;
167
512
}
168
169
/*
170
 * create a JWK struct from a cjose_jwk object
171
 */
172
11.9k
static oidc_jwk_t *oidc_jwk_from_cjose(apr_pool_t *pool, cjose_jwk_t *cjose_jwk, const char *use) {
173
11.9k
  cjose_err cjose_err;
174
11.9k
  oidc_jwk_t *jwk = oidc_jwk_new(pool);
175
11.9k
  jwk->cjose_jwk = cjose_jwk;
176
11.9k
  jwk->kid = apr_pstrdup(pool, cjose_jwk_get_kid(jwk->cjose_jwk, &cjose_err));
177
11.9k
  jwk->kty = cjose_jwk_get_kty(jwk->cjose_jwk, &cjose_err);
178
11.9k
  jwk->use = apr_pstrdup(pool, use);
179
11.9k
  return jwk;
180
11.9k
}
181
182
/*
183
 * parse a JSON string to a JWK struct
184
 */
185
54.9k
oidc_jwk_t *oidc_jwk_parse(apr_pool_t *pool, const json_t *json, oidc_jose_error_t *err) {
186
54.9k
  oidc_jwk_t *result = NULL;
187
54.9k
  cjose_jwk_t *cjose_jwk = NULL;
188
54.9k
  cjose_err cjose_err;
189
54.9k
  oidc_jose_error_t x5c_err;
190
54.9k
  char *use = NULL;
191
54.9k
  const json_t *v = NULL;
192
54.9k
  const json_t *e = NULL;
193
194
54.9k
  const char *s_json = oidc_json_encode(pool, json, OIDC_JSON_PRESERVE_ORDER | OIDC_JSON_COMPACT);
195
54.9k
  if (s_json == NULL) {
196
15.1k
    oidc_jose_error(err, "could not serialize JWK");
197
15.1k
    goto end;
198
15.1k
  }
199
200
39.8k
  cjose_jwk = cjose_jwk_import(s_json, _oidc_strlen(s_json), &cjose_err);
201
202
39.8k
  if (cjose_jwk == NULL) {
203
    // exception because x5c is not supported by cjose natively
204
    // ignore errors set by oidc_jwk_parse_x5c_spec
205
27.9k
    cjose_jwk = _oidc_jwk_parse_x5c_spec(pool, json, &x5c_err);
206
27.9k
    if (cjose_jwk == NULL) {
207
27.9k
      oidc_jose_error(err, "JWK parsing failed: %s", oidc_cjose_e2s(pool, cjose_err));
208
27.9k
      goto end;
209
27.9k
    }
210
27.9k
  }
211
212
11.9k
  oidc_jose_get_string(pool, json, OIDC_JOSE_JWK_USE_STR, FALSE, &use, NULL);
213
214
11.9k
  result = oidc_jwk_from_cjose(pool, cjose_jwk, use);
215
216
  // set alg
217
11.9k
  oidc_jose_get_string(pool, json, OIDC_JOSE_JWK_ALG_STR, FALSE, &result->alg, NULL);
218
219
  // set x5c array
220
11.9k
  v = json_object_get(json, OIDC_JOSE_JWK_X5C_STR);
221
11.9k
  if (v && json_is_array(v)) {
222
485
    result->x5c = apr_array_make(pool, (int)json_array_size(v), sizeof(const char *));
223
36.6k
    for (size_t i = 0; i < json_array_size(v); i++) {
224
36.1k
      e = json_array_get(v, i);
225
36.1k
      if (json_is_string(e))
226
26.2k
        APR_ARRAY_PUSH(result->x5c, const char *) = apr_pstrdup(pool, json_string_value(e));
227
36.1k
    }
228
485
  }
229
230
  // set x5t#256
231
11.9k
  v = json_object_get(json, OIDC_JOSE_JWK_X5T256_STR);
232
11.9k
  if (v)
233
336
    result->x5t_S256 = apr_pstrdup(pool, json_string_value(v));
234
235
  // set x5t
236
11.9k
  v = json_object_get(json, OIDC_JOSE_JWK_X5T_STR);
237
11.9k
  if (v)
238
207
    result->x5t = apr_pstrdup(pool, json_string_value(v));
239
240
54.9k
end:
241
242
54.9k
  return result;
243
11.9k
}
244
245
/*
246
 * copy a JWK by converting oidc_jwk_t to JSON and parsing it back
247
 */
248
9.41k
oidc_jwk_t *oidc_jwk_copy(apr_pool_t *pool, const oidc_jwk_t *src) {
249
9.41k
  cjose_err err;
250
9.41k
  oidc_jwk_t *dst = oidc_jwk_new(pool);
251
9.41k
  dst->cjose_jwk = cjose_jwk_retain(src->cjose_jwk, &err);
252
9.41k
  dst->kid = apr_pstrdup(pool, src->kid);
253
9.41k
  dst->kty = src->kty;
254
9.41k
  dst->use = apr_pstrdup(pool, src->use);
255
9.41k
  dst->alg = apr_pstrdup(pool, src->alg);
256
9.41k
  dst->x5c = NULL;
257
9.41k
  if (src->x5c) {
258
485
    dst->x5c = apr_array_make(pool, src->x5c->nelts, sizeof(const char *));
259
26.7k
    for (int i = 0; i < src->x5c->nelts; i++)
260
26.2k
      APR_ARRAY_PUSH(dst->x5c, const char *) = APR_ARRAY_IDX(src->x5c, i, const char *);
261
485
  }
262
9.41k
  dst->x5t = apr_pstrdup(pool, src->x5t);
263
9.41k
  dst->x5t_S256 = apr_pstrdup(pool, src->x5t_S256);
264
9.41k
  return dst;
265
9.41k
}
266
267
/*
268
 * destroy resources allocated for a JWK struct
269
 */
270
105k
void oidc_jwk_destroy(oidc_jwk_t *jwk) {
271
105k
  if (jwk && jwk->cjose_jwk) {
272
105k
    cjose_jwk_release(jwk->cjose_jwk);
273
105k
    jwk->cjose_jwk = NULL;
274
105k
  }
275
105k
}
276
277
/*
278
 * destroy a list of JWKs structs
279
 */
280
10.1k
void oidc_jwk_list_destroy_hash(apr_hash_t *keys) {
281
10.1k
  const void *key = NULL;
282
10.1k
  apr_ssize_t klen = 0;
283
10.1k
  if (keys == NULL)
284
0
    return;
285
10.1k
  for (apr_hash_index_t *hi = apr_hash_first(NULL, keys); hi; hi = apr_hash_next(hi)) {
286
12
    oidc_jwk_t *jwk = NULL;
287
12
    apr_hash_this(hi, &key, &klen, (void **)&jwk);
288
12
    oidc_jwk_destroy(jwk);
289
12
    apr_hash_set(keys, key, klen, NULL);
290
12
  }
291
10.1k
}
292
293
/*
294
 * copy a list (array) of JWKs
295
 */
296
4.69k
apr_array_header_t *oidc_jwk_list_copy(apr_pool_t *pool, apr_array_header_t *src) {
297
4.69k
  apr_array_header_t *dst = NULL;
298
299
4.69k
  if (src == NULL)
300
4.69k
    return NULL;
301
302
0
  dst = apr_array_make(pool, src->nelts, sizeof(const oidc_jwk_t *));
303
0
  for (int i = 0; i < src->nelts; i++)
304
0
    APR_ARRAY_PUSH(dst, oidc_jwk_t *) = oidc_jwk_copy(pool, APR_ARRAY_IDX(src, i, const oidc_jwk_t *));
305
306
0
  return dst;
307
4.69k
}
308
309
/*
310
 * destroy a list (array) of JWKs
311
 */
312
1.52k
void oidc_jwk_list_destroy(apr_array_header_t *keys_list) {
313
1.52k
  if (keys_list == NULL)
314
0
    return;
315
1.52k
  oidc_jwk_t **jwk = NULL;
316
13.1k
  while ((jwk = apr_array_pop(keys_list))) {
317
11.6k
    oidc_jwk_destroy(*jwk);
318
11.6k
  }
319
1.52k
}
320
321
/*
322
 * parse a JSON object in to a JWK struct
323
 */
324
54.9k
apr_byte_t oidc_jwk_parse_json(apr_pool_t *pool, const json_t *json, oidc_jwk_t **jwk, oidc_jose_error_t *err) {
325
54.9k
  *jwk = oidc_jwk_parse(pool, json, err);
326
54.9k
  return (*jwk != NULL);
327
54.9k
}
328
329
/*
330
 * parse a set of JWKs into a list (array) of JWK structs
331
 */
332
apr_byte_t oidc_jwks_parse_json(apr_pool_t *pool, const json_t *json, apr_array_header_t **jwk_list,
333
1.52k
        oidc_jose_error_t *err) {
334
1.52k
  const json_t *keys = json_object_get(json, OIDC_JOSE_JWKS_KEYS_STR);
335
1.52k
  if ((keys == NULL) || (!json_is_array(keys))) {
336
0
    oidc_jose_error(err, "JWKS did not contain \"" OIDC_JOSE_JWKS_KEYS_STR "\" array");
337
0
    return FALSE;
338
0
  }
339
1.52k
  *jwk_list = apr_array_make(pool, (int)json_array_size(keys), sizeof(const oidc_jwk_t *));
340
13.1k
  for (size_t i = 0; i < json_array_size(keys); i++) {
341
12.7k
    const json_t *elem = json_array_get(keys, i);
342
12.7k
    if (elem == NULL)
343
0
      continue;
344
12.7k
    oidc_jwk_t *jwk;
345
12.7k
    if (oidc_jwk_parse_json(pool, elem, &jwk, err) != TRUE) {
346
      /* the keys parsed so far wrap non-pooled cjose/OpenSSL objects: release them rather
347
       * than hand the caller a half-built list it has no reason to clean up after a failure */
348
1.15k
      oidc_jwk_list_destroy(*jwk_list);
349
1.15k
      *jwk_list = NULL;
350
1.15k
      return FALSE;
351
1.15k
    }
352
11.6k
    APR_ARRAY_PUSH(*jwk_list, const oidc_jwk_t *) = jwk;
353
11.6k
  }
354
373
  return TRUE;
355
1.52k
}
356
357
/*
358
 * check if a JSON object is a JWK
359
 */
360
3.73k
apr_byte_t oidc_is_jwk(const json_t *json) {
361
3.73k
  const json_t *kty = json_object_get(json, OIDC_JOSE_JWK_KTY_STR);
362
3.73k
  if ((kty == NULL) || (!json_is_string(kty))) {
363
45
    return FALSE;
364
45
  }
365
3.68k
  return TRUE;
366
3.73k
}
367
368
/*
369
 * check if a JSON object is a set JWKs
370
 */
371
5.25k
apr_byte_t oidc_is_jwks(const json_t *json) {
372
5.25k
  const json_t *keys = json_object_get(json, OIDC_JOSE_JWKS_KEYS_STR);
373
5.25k
  if ((keys == NULL) || (!json_is_array(keys))) {
374
3.73k
    return FALSE;
375
3.73k
  }
376
1.52k
  return TRUE;
377
5.25k
}
378
379
/*
380
 * produce the serialized JSON  JWK representation from an oidc_jwk_t structure
381
 */
382
9.68k
apr_byte_t oidc_jwk_to_json(apr_pool_t *pool, const oidc_jwk_t *jwk, char **s_json, oidc_jose_error_t *oidc_err) {
383
9.68k
  apr_byte_t rv = FALSE;
384
9.68k
  char *s_cjose = NULL;
385
9.68k
  cjose_err err;
386
9.68k
  json_t *json = NULL;
387
9.68k
  json_t *temp = NULL;
388
9.68k
  json_error_t json_error;
389
390
  // input sanity checks
391
9.68k
  if ((jwk == NULL) || (s_json == NULL))
392
0
    goto end;
393
394
  // get the JWK string representation from cjose
395
9.68k
  s_cjose = cjose_jwk_to_json(jwk->cjose_jwk, TRUE, &err);
396
9.68k
  if (s_cjose == NULL) {
397
0
    oidc_jose_error(oidc_err, "oidc_jwk_to_json: cjose_jwk_to_json failed: %s", oidc_cjose_e2s(pool, err));
398
0
    goto end;
399
0
  }
400
401
9.68k
  json = json_loads(s_cjose, 0, &json_error);
402
9.68k
  if (json == NULL) {
403
0
    oidc_jose_error(oidc_err, "oidc_jwk_to_json: json_loads failed");
404
0
    goto end;
405
0
  }
406
407
9.68k
  if (jwk->use)
408
1.75k
    json_object_set_new(json, OIDC_JOSE_JWK_USE_STR, json_string(jwk->use));
409
410
  // set alg (RFC 7517 section 4.4); lets an OP pick this key for the named algorithm
411
9.68k
  if (jwk->alg)
412
385
    json_object_set_new(json, OIDC_JOSE_JWK_ALG_STR, json_string(jwk->alg));
413
414
  // set x5c
415
9.68k
  if ((jwk->x5c != NULL) && (jwk->x5c->nelts > 0)) {
416
412
    temp = json_array();
417
45.6k
    for (int i = 0; i < jwk->x5c->nelts; i++)
418
45.2k
      json_array_append_new(temp, json_string(APR_ARRAY_IDX(jwk->x5c, i, const char *)));
419
412
    json_object_set_new(json, OIDC_JOSE_JWK_X5C_STR, temp);
420
412
  }
421
422
  // set x5t#256
423
9.68k
  if (jwk->x5t_S256 != NULL)
424
364
    json_object_set_new(json, OIDC_JOSE_JWK_X5T256_STR, json_string(jwk->x5t_S256));
425
426
  // set x5t
427
9.68k
  if (jwk->x5t != NULL)
428
359
    json_object_set_new(json, OIDC_JOSE_JWK_X5T_STR, json_string(jwk->x5t));
429
430
  // generate the string ...
431
9.68k
  *s_json = oidc_json_encode(pool, json, OIDC_JSON_ENCODE_ANY | OIDC_JSON_COMPACT | OIDC_JSON_PRESERVE_ORDER);
432
433
9.68k
  rv = (*s_json != NULL);
434
435
9.68k
end:
436
437
9.68k
  if (json)
438
9.68k
    json_decref(json);
439
9.68k
  if (s_cjose)
440
9.68k
    cjose_get_dealloc()(s_cjose);
441
442
9.68k
  return rv;
443
9.68k
}
444
445
/*
446
 * convert the public part of a JWK struct to a (pool-allocated) JSON string; unlike oidc_jwk_to_json this
447
 * excludes private key material, which is required when publishing a key (e.g. the DPoP confirmation header)
448
 */
449
9.68k
apr_byte_t oidc_jwk_to_public_json(apr_pool_t *pool, const oidc_jwk_t *jwk, char **s_json, oidc_jose_error_t *err) {
450
9.68k
  cjose_err cjose_err;
451
9.68k
  char *s_cjose = NULL;
452
453
9.68k
  if ((jwk == NULL) || (s_json == NULL))
454
0
    return FALSE;
455
456
9.68k
  s_cjose = cjose_jwk_to_json(jwk->cjose_jwk, FALSE /* public only */, &cjose_err);
457
9.68k
  if (s_cjose == NULL) {
458
0
    oidc_jose_error(err, "cjose_jwk_to_json failed: %s", oidc_cjose_e2s(pool, cjose_err));
459
0
    return FALSE;
460
0
  }
461
9.68k
  *s_json = apr_pstrdup(pool, s_cjose);
462
9.68k
  cjose_get_dealloc()(s_cjose);
463
464
9.68k
  return TRUE;
465
9.68k
}
466
467
/*
468
 * derive the default JWS signing algorithm for a key (RSA -> RS256; EC -> ES256/384/512 per curve);
469
 * returns NULL when the key type/curve is unsupported
470
 */
471
9.68k
const char *oidc_jwk_default_jws_alg(const oidc_jwk_t *jwk) {
472
9.68k
  if (jwk == NULL)
473
0
    return NULL;
474
9.68k
  if (jwk->kty == OIDC_JOSE_JWK_KTY_RSA)
475
1.60k
    return OIDC_JOSE_HDR_ALG_RS256;
476
8.08k
  if (jwk->kty == OIDC_JOSE_JWK_KTY_EC) {
477
1.42k
    if (cjose_jwk_EC_get_curve(jwk->cjose_jwk, NULL) == NID_X9_62_prime256v1)
478
640
      return OIDC_JOSE_HDR_ALG_ES256;
479
788
    if (cjose_jwk_EC_get_curve(jwk->cjose_jwk, NULL) == NID_secp384r1)
480
313
      return OIDC_JOSE_HDR_ALG_ES384;
481
475
    if (cjose_jwk_EC_get_curve(jwk->cjose_jwk, NULL) == NID_secp521r1)
482
475
      return OIDC_JOSE_HDR_ALG_ES512;
483
475
  }
484
6.65k
  return NULL;
485
8.08k
}
486
487
/*
488
 * set a specified key identifier or generate a key identifier and set it
489
 */
490
static apr_byte_t oidc_jwk_set_or_generate_kid(apr_pool_t *pool, cjose_jwk_t *cjose_jwk, const char *s_kid,
491
41.4k
                 const char *key_params, int key_params_len, oidc_jose_error_t *err) {
492
493
41.4k
  char *jwk_kid = NULL;
494
495
41.4k
  if (s_kid != NULL) {
496
280
    jwk_kid = apr_pstrdup(pool, s_kid);
497
41.1k
  } else {
498
    /* calculate a unique key identifier (kid) by fingerprinting the key params */
499
41.1k
    if (oidc_jose_hash_and_base64url_encode(pool, OIDC_JOSE_ALG_SHA256, key_params, key_params_len,
500
41.1k
              &jwk_kid, err) == FALSE) {
501
0
      return FALSE;
502
0
    }
503
41.1k
  }
504
505
41.4k
  cjose_err cjose_err;
506
41.4k
  if (cjose_jwk_set_kid(cjose_jwk, jwk_kid, _oidc_strlen(jwk_kid), &cjose_err) == FALSE) {
507
0
    oidc_jose_error(err, "cjose_jwk_set_kid failed: %s", oidc_cjose_e2s(pool, cjose_err));
508
0
    return FALSE;
509
0
  }
510
511
41.4k
  return TRUE;
512
41.4k
}
513
514
/*
515
 * create an "oct" symmetric JWK
516
 */
517
oidc_jwk_t *oidc_jwk_create_symmetric_key(apr_pool_t *pool, const char *skid, const unsigned char *key,
518
83.5k
            unsigned int key_len, apr_byte_t set_kid, oidc_jose_error_t *err) {
519
520
83.5k
  cjose_err cjose_err;
521
83.5k
  cjose_jwk_t *cjose_jwk = cjose_jwk_create_oct_spec(key, key_len, &cjose_err);
522
83.5k
  if (cjose_jwk == NULL) {
523
0
    oidc_jose_error(err, "cjose_jwk_create_oct_spec failed: %s", oidc_cjose_e2s(pool, cjose_err));
524
0
    return NULL;
525
0
  }
526
527
83.5k
  if ((set_kid == TRUE) &&
528
41.1k
      (oidc_jwk_set_or_generate_kid(pool, cjose_jwk, skid, (const char *)key, key_len, err) == FALSE)) {
529
0
    cjose_jwk_release(cjose_jwk);
530
0
    return NULL;
531
0
  }
532
533
83.5k
  oidc_jwk_t *jwk = oidc_jwk_new(pool);
534
83.5k
  jwk->cjose_jwk = cjose_jwk;
535
83.5k
  jwk->kid = apr_pstrdup(pool, cjose_jwk_get_kid(jwk->cjose_jwk, &cjose_err));
536
83.5k
  jwk->kty = cjose_jwk_get_kty(jwk->cjose_jwk, &cjose_err);
537
83.5k
  return jwk;
538
83.5k
}
539
540
/*
541
 * read an x509 certificate and its public key from the provided input
542
 */
543
static apr_byte_t oidc_jwk_x509_read(apr_pool_t *pool, BIO *input, char **encoded_certificate, EVP_PKEY **pkey,
544
21.0k
             X509 **rx509, oidc_jose_error_t *err) {
545
21.0k
  apr_byte_t rv = FALSE;
546
21.0k
  X509 *x509 = NULL;
547
21.0k
  int encoded_cert_len = 0;
548
549
  /* read the X.509 struct - assume input is no public key */
550
21.0k
  if ((x509 = PEM_read_bio_X509_AUX(input, NULL, NULL, NULL)) == NULL) {
551
2.02k
    oidc_jose_error_openssl(err, "PEM_read_bio_X509_AUX");
552
2.02k
    goto end;
553
2.02k
  }
554
555
19.0k
  if (pkey) {
556
    /* get the public key struct from the X.509 struct */
557
224
    *pkey = X509_get_pubkey(x509);
558
224
    if (*pkey == NULL) {
559
62
      oidc_jose_error_openssl(err, "X509_get_pubkey");
560
62
      goto end;
561
62
    }
562
224
  }
563
564
  /* populate x5c certificate */
565
19.0k
  encoded_cert_len = oidc_jose_util_get_b64encoded_certificate_data(pool, x509, encoded_certificate, err);
566
567
19.0k
  rv = (encoded_certificate != NULL) && (encoded_cert_len > 0);
568
569
21.0k
end:
570
21.0k
  if (x509) {
571
19.0k
    if (rx509)
572
224
      *rx509 = x509;
573
18.8k
    else
574
18.8k
      X509_free(x509);
575
19.0k
  }
576
21.0k
  return rv;
577
19.0k
}
578
579
/*
580
 * extract a JWK struct and a fingerprint from an OpenSSL RSA key
581
 */
582
static apr_byte_t _oidc_jwk_rsa_key_to_jwk(apr_pool_t *pool, const EVP_PKEY *pkey, oidc_jwk_t **oidc_jwk, char **fp,
583
77
             int *fp_len, oidc_jose_error_t *err) {
584
77
  apr_byte_t rv = FALSE;
585
77
  cjose_err cjose_err;
586
77
  BIGNUM *rsa_n = NULL;
587
77
  BIGNUM *rsa_e = NULL;
588
77
  BIGNUM *rsa_d = NULL;
589
77
  cjose_jwk_rsa_keyspec key_spec;
590
591
77
  _oidc_memset(&key_spec, 0, sizeof(cjose_jwk_rsa_keyspec));
592
593
77
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
594
  /* the modulus and exponent are required; a NULL BIGNUM here would crash BN_num_bytes() below */
595
77
  if (!EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_RSA_N, &rsa_n) ||
596
77
      !EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_RSA_E, &rsa_e)) {
597
0
    oidc_jose_error_openssl(err, "EVP_PKEY_get_bn_param(OSSL_PKEY_PARAM_RSA_N/E)");
598
0
    goto end;
599
0
  }
600
77
  EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_RSA_D, &rsa_d);
601
#else
602
  /* get the RSA key from the public key struct */
603
  RSA *rsa = (RSA *)EVP_PKEY_get1_RSA((EVP_PKEY *)pkey);
604
  if (rsa == NULL) {
605
    oidc_jose_error_openssl(err, "EVP_PKEY_get1_RSA");
606
    goto end;
607
  }
608
609
#if OPENSSL_VERSION_NUMBER >= 0x10100005L && !defined(LIBRESSL_VERSION_NUMBER)
610
  RSA_get0_key(rsa, (const BIGNUM **)&rsa_n, (const BIGNUM **)&rsa_e, (const BIGNUM **)&rsa_d);
611
#else
612
  rsa_n = rsa->n;
613
  rsa_e = rsa->e;
614
  rsa_d = rsa->d;
615
#endif
616
617
  RSA_free(rsa);
618
#endif
619
620
  /* convert the modulus bignum in to a key/len */
621
77
  key_spec.nlen = BN_num_bytes(rsa_n);
622
77
  key_spec.n = apr_pcalloc(pool, key_spec.nlen);
623
77
  BN_bn2bin(rsa_n, key_spec.n);
624
625
  /* convert the exponent bignum in to a key/len */
626
77
  key_spec.elen = BN_num_bytes(rsa_e);
627
77
  key_spec.e = apr_pcalloc(pool, key_spec.elen);
628
77
  BN_bn2bin(rsa_e, key_spec.e);
629
630
  /* convert the private exponent bignum in to a key/len */
631
77
  if (rsa_d != NULL) {
632
26
    key_spec.dlen = BN_num_bytes(rsa_d);
633
26
    key_spec.d = apr_pcalloc(pool, key_spec.dlen);
634
26
    BN_bn2bin(rsa_d, key_spec.d);
635
26
  }
636
637
77
  (*oidc_jwk)->cjose_jwk = cjose_jwk_create_RSA_spec(&key_spec, &cjose_err);
638
77
  if ((*oidc_jwk)->cjose_jwk == NULL) {
639
13
    oidc_jose_error(err, "cjose_jwk_create_RSA_spec failed: %s", oidc_cjose_e2s(pool, cjose_err));
640
13
    goto end;
641
13
  }
642
643
64
  *fp_len = (int)(key_spec.nlen + key_spec.elen);
644
64
  *fp = apr_pcalloc(pool, *fp_len);
645
64
  _oidc_memcpy(*fp, key_spec.n, key_spec.nlen);
646
64
  _oidc_memcpy(*fp + key_spec.nlen, key_spec.e, key_spec.elen);
647
648
64
  rv = TRUE;
649
650
77
end:
651
652
77
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
653
77
  if (rsa_n)
654
77
    BN_clear_free(rsa_n);
655
77
  if (rsa_e)
656
77
    BN_clear_free(rsa_e);
657
77
  if (rsa_d)
658
26
    BN_clear_free(rsa_d);
659
77
#endif
660
661
77
  return rv;
662
64
}
663
664
#if (OIDC_JOSE_EC_SUPPORT)
665
666
/*
667
 * extract a JWK struct and a fingerprint from an OpenSSL Elliptic Curve key
668
 */
669
static apr_byte_t _oidc_jwk_ec_key_to_jwk(apr_pool_t *pool, const EVP_PKEY *pkey, oidc_jwk_t **oidc_jwk, char **fp,
670
222
            int *fp_len, oidc_jose_error_t *err) {
671
222
  apr_byte_t rv = FALSE;
672
222
  cjose_err cjose_err;
673
222
  cjose_jwk_ec_keyspec ec_keyspec;
674
222
  int crv = 0;
675
222
  BIGNUM *ec_x = NULL;
676
222
  BIGNUM *ec_y = NULL;
677
222
  BIGNUM *ec_d = NULL;
678
679
222
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
680
222
  char curve_name[64];
681
222
  size_t curve_name_len = 0;
682
683
  /* a parseable EC key can lack a public point (no affine coordinates); see OSS-Fuzz issue 550951150 */
684
222
  if (!EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_EC_PUB_X, &ec_x) ||
685
219
      !EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_EC_PUB_Y, &ec_y)) {
686
3
    oidc_jose_error_openssl(err, "EVP_PKEY_get_bn_param(OSSL_PKEY_PARAM_EC_PUB_X/Y)");
687
3
    goto end;
688
3
  }
689
219
  EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_PRIV_KEY, &ec_d);
690
219
  if (!EVP_PKEY_get_utf8_string_param(pkey, OSSL_PKEY_PARAM_GROUP_NAME, curve_name, sizeof(curve_name),
691
219
              &curve_name_len)) {
692
0
    oidc_jose_error_openssl(err, "EVP_PKEY_get_utf8_string_param(OSSL_PKEY_PARAM_GROUP_NAME)");
693
0
    goto end;
694
0
  }
695
219
  crv = OBJ_sn2nid(curve_name);
696
#else
697
  EC_KEY *eckey = (EC_KEY *)EVP_PKEY_get1_EC_KEY((EVP_PKEY *)pkey);
698
  if (eckey == NULL) {
699
    oidc_jose_error_openssl(err, "EVP_PKEY_get1_EC_KEY");
700
    goto end;
701
  }
702
  const EC_GROUP *ec_group = EC_KEY_get0_group(eckey);
703
  const EC_POINT *ecpoint = EC_KEY_get0_public_key(eckey);
704
  crv = EC_GROUP_get_curve_name(ec_group);
705
  ec_x = BN_new();
706
  ec_y = BN_new();
707
  if (EC_POINT_get_affine_coordinates_GFp(ec_group, ecpoint, ec_x, ec_y, NULL) == 0) {
708
    oidc_jose_error_openssl(err, "EC_POINT_get_affine_coordinates_GFp");
709
    EC_KEY_free(eckey);
710
    goto end;
711
  }
712
  /* NB: ec_d borrows from eckey; releasing our get1 reference here is safe since pkey keeps it alive */
713
  ec_d = (BIGNUM *)EC_KEY_get0_private_key(eckey);
714
  if (crv == 0) {
715
    oidc_jose_error_openssl(err, "EC_GROUP_get_curve_name");
716
    EC_KEY_free(eckey);
717
    goto end;
718
  }
719
  EC_KEY_free(eckey);
720
#endif
721
722
219
  _oidc_memset(&ec_keyspec, 0, sizeof(cjose_jwk_ec_keyspec));
723
724
219
  ec_keyspec.crv = crv;
725
726
219
  ec_keyspec.xlen = BN_num_bytes(ec_x);
727
219
  ec_keyspec.x = apr_pcalloc(pool, ec_keyspec.xlen);
728
219
  BN_bn2bin(ec_x, ec_keyspec.x);
729
730
219
  ec_keyspec.ylen = BN_num_bytes(ec_y);
731
219
  ec_keyspec.y = apr_pcalloc(pool, ec_keyspec.ylen);
732
219
  BN_bn2bin(ec_y, ec_keyspec.y);
733
734
219
  if (ec_d != NULL) {
735
86
    ec_keyspec.dlen = BN_num_bytes(ec_d);
736
86
    ec_keyspec.d = apr_pcalloc(pool, ec_keyspec.dlen);
737
86
    BN_bn2bin(ec_d, ec_keyspec.d);
738
86
  }
739
740
219
  (*oidc_jwk)->cjose_jwk = cjose_jwk_create_EC_spec(&ec_keyspec, &cjose_err);
741
219
  if ((*oidc_jwk)->cjose_jwk == NULL) {
742
2
    oidc_jose_error(err, "cjose_jwk_create_EC_spec failed: %s", oidc_cjose_e2s(pool, cjose_err));
743
2
    goto end;
744
2
  }
745
746
217
  apr_uint32_t b = htonl(crv);
747
217
  *fp_len = (int)(sizeof(b) + ec_keyspec.xlen + ec_keyspec.ylen);
748
217
  *fp = apr_pcalloc(pool, *fp_len);
749
217
  _oidc_memcpy(*fp, &b, sizeof(b));
750
217
  _oidc_memcpy(*fp + sizeof(b), ec_keyspec.x, ec_keyspec.xlen);
751
217
  _oidc_memcpy(*fp + sizeof(b) + ec_keyspec.xlen, ec_keyspec.y, ec_keyspec.ylen);
752
753
217
  rv = TRUE;
754
755
222
end:
756
757
222
  if (ec_x)
758
219
    BN_clear_free(ec_x);
759
222
  if (ec_y)
760
219
    BN_clear_free(ec_y);
761
222
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
762
222
  if (ec_d)
763
86
    BN_clear_free(ec_d);
764
222
#endif
765
222
  return rv;
766
217
}
767
768
#endif
769
770
/*
771
 * convert the PEM public key - possibly in a X.509 certificate - in the BIO pointed to
772
 * by "input" to a JSON Web Key object
773
 */
774
/*
775
 * populate x5c (first cert plus any trailing chain entries) and the x5t/x5t#S256
776
 * thumbprints on jwk from a parsed X.509 certificate
777
 */
778
static apr_byte_t oidc_jwk_populate_cert_info(apr_pool_t *pool, BIO *input, oidc_jwk_t *jwk, const X509 *x509,
779
162
                const char *first_pem, oidc_jose_error_t *err) {
780
162
  unsigned char *x509_bytes = NULL;
781
162
  int x509_cert_length = 0;
782
162
  char *next_pem = NULL;
783
162
  apr_byte_t rv = FALSE;
784
785
162
  jwk->x5c = apr_array_make(pool, 1, sizeof(const char *));
786
162
  if (jwk->x5c == NULL) {
787
0
    oidc_jose_error(err, "apr_array_make failed");
788
0
    return FALSE;
789
0
  }
790
162
  APR_ARRAY_PUSH(jwk->x5c, const char *) = first_pem;
791
792
#if OPENSSL_VERSION_NUMBER < 0x000907000L
793
  // openssl below 0.9.7 does not allocate memory for you :o
794
  x509_cert_length = i2d_X509((X509 *)x509, NULL);
795
  if (x509_cert_length <= 0) {
796
    oidc_jose_error_openssl(err, "i2d_X509");
797
    goto end;
798
  }
799
  x509_bytes = (unsigned char *)OPENSSL_malloc(pool, x509_cert_length + 1);
800
  const unsigned char *p = x509_bytes;
801
  x509_cert_length = i2d_X509((X509 *)x509, &p);
802
#else
803
162
  x509_cert_length = i2d_X509((X509 *)x509, &x509_bytes);
804
162
#endif
805
162
  if (x509_cert_length < 0) {
806
0
    oidc_jose_error_openssl(err, "i2d_X509");
807
0
    goto end;
808
0
  }
809
810
162
  oidc_jose_hash_and_base64url_encode(pool, OIDC_JOSE_ALG_SHA1, (const char *)x509_bytes, x509_cert_length,
811
162
              &jwk->x5t, err);
812
162
  oidc_jose_hash_and_base64url_encode(pool, OIDC_JOSE_ALG_SHA256, (const char *)x509_bytes, x509_cert_length,
813
162
              &jwk->x5t_S256, err);
814
815
19.0k
  while (oidc_jwk_x509_read(pool, input, &next_pem, NULL, NULL, err) == TRUE)
816
18.8k
    APR_ARRAY_PUSH(jwk->x5c, const char *) = next_pem;
817
818
162
  rv = TRUE;
819
820
162
end:
821
162
  if (x509_bytes)
822
162
    OPENSSL_free(x509_bytes);
823
162
  return rv;
824
162
}
825
826
/* Read a bare PEM key or fall back to X.509; callers free a returned out_x509. */
827
static apr_byte_t oidc_jwk_pem_bio_read_public(apr_pool_t *pool, BIO *input, oidc_jwk_t *jwk, EVP_PKEY **pkey,
828
2.10k
                 X509 **out_x509, oidc_jose_error_t *err) {
829
830
2.10k
  *pkey = PEM_read_bio_PUBKEY(input, NULL, NULL, NULL);
831
2.10k
  if (*pkey != NULL)
832
22
    return TRUE;
833
834
  /* not a public key - reset the buffer and try as a certificate */
835
2.10k
  BIO_reset(input);
836
837
2.08k
  char *first_pem = NULL;
838
2.08k
  if (oidc_jwk_x509_read(pool, input, &first_pem, pkey, out_x509, err) == FALSE)
839
1.92k
    return FALSE;
840
841
162
  return oidc_jwk_populate_cert_info(pool, input, jwk, *out_x509, first_pem, err);
842
2.08k
}
843
844
/*
845
 * return the OpenSSL key-type base id for the supplied EVP_PKEY, abstracting
846
 * over the different OpenSSL API versions
847
 */
848
299
static int oidc_jwk_pkey_base_id(const EVP_PKEY *pkey) {
849
299
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
850
299
  return EVP_PKEY_get_base_id(pkey);
851
#elif (OPENSSL_VERSION_NUMBER > 0x10100000)
852
  return EVP_PKEY_base_id(pkey);
853
#else
854
  return EVP_PKEY_type(pkey->type);
855
#endif
856
299
}
857
858
/*
859
 * dispatch a parsed EVP_PKEY into the matching JWK builder, producing the
860
 * fingerprint bytes that drive kid generation
861
 */
862
static apr_byte_t oidc_jwk_pkey_to_jwk(apr_pool_t *pool, const EVP_PKEY *pkey, oidc_jwk_t **jwk, char **fp, int *fp_len,
863
299
               oidc_jose_error_t *err) {
864
299
  switch (oidc_jwk_pkey_base_id(pkey)) {
865
77
  case EVP_PKEY_RSA:
866
77
    return _oidc_jwk_rsa_key_to_jwk(pool, pkey, jwk, fp, fp_len, err);
867
0
#if (OIDC_JOSE_EC_SUPPORT)
868
222
  case EVP_PKEY_EC:
869
222
    return _oidc_jwk_ec_key_to_jwk(pool, pkey, jwk, fp, fp_len, err);
870
0
#endif
871
0
  default:
872
0
    oidc_jose_error(err, "unhandled key type: %d", oidc_jwk_pkey_base_id(pkey));
873
0
    return FALSE;
874
299
  }
875
299
}
876
877
apr_byte_t oidc_jwk_pem_bio_to_jwk(apr_pool_t *pool, BIO *input, const char *kid, oidc_jwk_t **oidc_jwk,
878
3.70k
           apr_byte_t is_private_key, oidc_jose_error_t *err) {
879
3.70k
  cjose_err cjose_err;
880
3.70k
  X509 *x509 = NULL;
881
3.70k
  EVP_PKEY *pkey = NULL;
882
3.70k
  apr_byte_t rv = FALSE;
883
3.70k
  char *fp = NULL;
884
3.70k
  int fp_len = 0;
885
886
3.70k
  *oidc_jwk = oidc_jwk_new(pool);
887
888
3.70k
  if (is_private_key == TRUE) {
889
1.59k
    if ((pkey = PEM_read_bio_PrivateKey(input, NULL, NULL, NULL)) == NULL) {
890
1.48k
      oidc_jose_error_openssl(err, "PEM_read_bio_PrivateKey");
891
1.48k
      goto end;
892
1.48k
    }
893
2.10k
  } else if (oidc_jwk_pem_bio_read_public(pool, input, *oidc_jwk, &pkey, &x509, err) == FALSE) {
894
1.92k
    goto end;
895
1.92k
  }
896
897
299
  if (oidc_jwk_pkey_to_jwk(pool, pkey, oidc_jwk, &fp, &fp_len, err) == FALSE)
898
18
    goto end;
899
900
281
  if (oidc_jwk_set_or_generate_kid(pool, (*oidc_jwk)->cjose_jwk, kid, fp, fp_len, err) == FALSE)
901
0
    goto end;
902
903
281
  (*oidc_jwk)->kid = apr_pstrdup(pool, cjose_jwk_get_kid((*oidc_jwk)->cjose_jwk, &cjose_err));
904
281
  (*oidc_jwk)->kty = cjose_jwk_get_kty((*oidc_jwk)->cjose_jwk, &cjose_err);
905
906
281
  rv = TRUE;
907
908
3.70k
end:
909
910
3.70k
  if (pkey)
911
299
    EVP_PKEY_free(pkey);
912
3.70k
  if (x509)
913
224
    X509_free(x509);
914
915
3.70k
  return rv;
916
281
}
917
918
/*
919
 * parse a PEM-formatted public or private key from the specified file
920
 */
921
static apr_byte_t oidc_jwk_parse_pem_key(apr_pool_t *pool, apr_byte_t is_private_key, const char *kid,
922
0
           const char *filename, oidc_jwk_t **jwk, oidc_jose_error_t *err) {
923
0
  BIO *input = NULL;
924
0
  apr_byte_t rv = FALSE;
925
926
0
  if ((input = BIO_new(BIO_s_file())) == NULL) {
927
0
    oidc_jose_error_openssl(err, "BIO_new/BIO_s_file");
928
0
    goto end;
929
0
  }
930
931
0
  if (BIO_read_filename(input, filename) <= 0) {
932
0
    oidc_jose_error_openssl(err, "BIO_read_filename");
933
0
    goto end;
934
0
  }
935
936
0
  if (oidc_jwk_pem_bio_to_jwk(pool, input, kid, jwk, is_private_key, err) == FALSE)
937
0
    goto end;
938
939
0
  rv = TRUE;
940
941
0
end:
942
943
0
  if (input)
944
0
    BIO_free(input);
945
946
0
  return rv;
947
0
}
948
949
1.01k
#define OIDC_JOSE_CERT_BEGIN "-----BEGIN CERTIFICATE-----"
950
1.01k
#define OIDC_JOSE_CERT_END "-----END CERTIFICATE-----"
951
952
/*
953
 * parse a PEM-formatted key from a JSON object in to a cjose JWK object
954
 */
955
512
static apr_byte_t _oidc_jwk_parse_x5c(apr_pool_t *pool, const json_t *json, cjose_jwk_t **jwk, oidc_jose_error_t *err) {
956
957
512
  apr_byte_t rv = FALSE;
958
512
  const char *kid = NULL;
959
512
  oidc_jwk_t *oidc_jwk = NULL;
960
961
  /* get the "x5c" array element from the JSON object */
962
512
  const json_t *v = json_object_get(json, OIDC_JOSE_HDR_X5C);
963
512
  if (v == NULL) {
964
0
    oidc_jose_error(err, "JSON key \"%s\" could not be found", OIDC_JOSE_HDR_X5C);
965
0
    return FALSE;
966
0
  }
967
512
  if (!json_is_array(v)) {
968
1
    oidc_jose_error(err, "JSON key \"%s\" was found but its value is not a JSON array", OIDC_JOSE_HDR_X5C);
969
1
    return FALSE;
970
1
  }
971
972
  /* take the first element of the array */
973
511
  v = json_array_get(v, 0);
974
511
  if (v == NULL) {
975
1
    oidc_jose_error(err, "first element in JSON array is \"null\"");
976
1
    return FALSE;
977
1
  }
978
510
  if (!json_is_string(v)) {
979
4
    oidc_jose_error(err, "first element in array is not a JSON string");
980
4
    return FALSE;
981
4
  }
982
983
506
  const char *s_x5c = json_string_value(v);
984
985
  /*
986
   * PEM-format it: the header, the base64 data in lines of at most "chunk" characters, the footer.
987
   * The result is sized and allocated once up front: the x5c value comes from the provider's JWKS
988
   * (up to the HTTP response limit) and re-printing the accumulated string per line, as was done
989
   * before, made the pool memory consumed by this function quadratic in its length (OSS-Fuzz 551146117)
990
   */
991
506
  const size_t chunk = 75;
992
506
  const size_t n = _oidc_strlen(s_x5c);
993
506
  const size_t n_lines = (n + chunk - 1) / chunk;
994
506
  const size_t len = _oidc_strlen(OIDC_JOSE_CERT_BEGIN) + 1 + n + n_lines + _oidc_strlen(OIDC_JOSE_CERT_END) + 1;
995
506
  char *s = apr_palloc(pool, len + 1);
996
506
  char *p = s;
997
998
506
  _oidc_memcpy(p, OIDC_JOSE_CERT_BEGIN, _oidc_strlen(OIDC_JOSE_CERT_BEGIN));
999
506
  p += _oidc_strlen(OIDC_JOSE_CERT_BEGIN);
1000
506
  *p++ = '\n';
1001
55.5k
  for (size_t i = 0; i < n; i += chunk) {
1002
55.0k
    const size_t m = ((n - i) < chunk) ? (n - i) : chunk;
1003
55.0k
    _oidc_memcpy(p, s_x5c + i, m);
1004
55.0k
    p += m;
1005
55.0k
    *p++ = '\n';
1006
55.0k
  }
1007
506
  _oidc_memcpy(p, OIDC_JOSE_CERT_END, _oidc_strlen(OIDC_JOSE_CERT_END));
1008
506
  p += _oidc_strlen(OIDC_JOSE_CERT_END);
1009
506
  *p++ = '\n';
1010
506
  *p = '\0';
1011
1012
  /* put it in BIO memory */
1013
506
  BIO *input = BIO_new_mem_buf(s, (int)(p - s));
1014
506
  if (input == NULL) {
1015
0
    oidc_jose_error_openssl(err, "BIO_new_mem_buf");
1016
0
    return FALSE;
1017
0
  }
1018
1019
506
  v = json_object_get(json, CJOSE_HDR_KID);
1020
506
  if ((v != NULL) && json_is_string(v)) {
1021
45
    kid = json_string_value(v);
1022
45
  }
1023
1024
  /* do the actual parsing */
1025
1026
506
  rv = oidc_jwk_pem_bio_to_jwk(pool, input, kid, &oidc_jwk, FALSE, err);
1027
506
  *jwk = oidc_jwk->cjose_jwk;
1028
1029
506
  BIO_free(input);
1030
1031
506
  return rv;
1032
506
}
1033
1034
/*
1035
 * parse a PEM formatted private key to a JWK
1036
 */
1037
apr_byte_t oidc_jwk_parse_pem_private_key(apr_pool_t *pool, const char *kid, const char *filename, oidc_jwk_t **jwk,
1038
0
            oidc_jose_error_t *err) {
1039
0
  return oidc_jwk_parse_pem_key(pool, TRUE, kid, filename, jwk, err);
1040
0
}
1041
1042
/*
1043
 * parse a PEM formatted public key file to a JWK
1044
 */
1045
apr_byte_t oidc_jwk_parse_pem_public_key(apr_pool_t *pool, const char *kid, const char *filename, oidc_jwk_t **jwk,
1046
0
           oidc_jose_error_t *err) {
1047
0
  return oidc_jwk_parse_pem_key(pool, FALSE, kid, filename, jwk, err);
1048
0
}