/src/mod_auth_openidc/src/jose.h
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 Object Signing and Encryption |
42 | | * |
43 | | * @Author: Hans Zandbelt - hans.zandbelt@openidc.com |
44 | | */ |
45 | | |
46 | | #ifndef _MOD_AUTH_OPENIDC_JOSE_H_ |
47 | | #define _MOD_AUTH_OPENIDC_JOSE_H_ |
48 | | |
49 | | #include "const.h" |
50 | | |
51 | | #include <apr_hash.h> |
52 | | #include <apr_pools.h> |
53 | | #include <apr_strings.h> |
54 | | #include <apr_tables.h> |
55 | | |
56 | | #include "json.h" |
57 | | |
58 | | /* opaque forward declaration of the OpenSSL BIO type used in the oidc_jwk_pem_bio_to_jwk() prototype below */ |
59 | | typedef struct bio_st BIO; |
60 | | |
61 | | #ifndef APR_ARRAY_IDX |
62 | | #define APR_ARRAY_IDX(ary, i, type) (((type *)(ary)->elts)[i]) |
63 | | #endif |
64 | | |
65 | | #ifndef APR_ARRAY_PUSH |
66 | | #define APR_ARRAY_PUSH(ary, type) (*((type *)apr_array_push(ary))) |
67 | | #endif |
68 | | |
69 | 0 | #define OIDC_JOSE_ALG_SHA1 "sha1" |
70 | 0 | #define OIDC_JOSE_ALG_SHA256 "sha256" |
71 | | |
72 | | /* indicate support for OpenSSL version dependent features */ |
73 | | #define OIDC_JOSE_EC_SUPPORT OPENSSL_VERSION_NUMBER >= 0x1000100f |
74 | | #define OIDC_JOSE_GCM_SUPPORT OPENSSL_VERSION_NUMBER >= 0x1000100f |
75 | | |
76 | | /* error message element sizes */ |
77 | 108k | #define OIDC_JOSE_ERROR_TEXT_LENGTH 200 |
78 | | #define OIDC_JOSE_ERROR_SOURCE_LENGTH 80 |
79 | | #define OIDC_JOSE_ERROR_FUNCTION_LENGTH 80 |
80 | | |
81 | | /* the OIDC jwk fields as references in RFC 5741 */ |
82 | | #define OIDC_JOSE_JWK_KID_STR "kid" // Key ID |
83 | 5 | #define OIDC_JOSE_JWK_KTY_STR "kty" // Key type |
84 | 0 | #define OIDC_JOSE_JWK_USE_STR "use" // Key usage (enc|sig) |
85 | 0 | #define OIDC_JOSE_JWK_X5C_STR "x5c" // X509 certificate chain |
86 | 0 | #define OIDC_JOSE_JWK_X5T_STR "x5t" // X509 SHA-1 thumbprint |
87 | 0 | #define OIDC_JOSE_JWK_X5T256_STR "x5t#S256" // X509 SHA-256 thumbprint |
88 | 0 | #define OIDC_JOSE_JWK_SIG_STR "sig" // use signature type |
89 | 0 | #define OIDC_JOSE_JWK_ENC_STR "enc" // use encryption type |
90 | 0 | #define OIDC_JOSE_JWK_ALG_STR "alg" // algorithm intended for use with the key |
91 | | |
92 | | /* the OIDC jwks fields from RFC 5741 */ |
93 | 5.01k | #define OIDC_JOSE_JWKS_KEYS_STR "keys" // Array of JWKs |
94 | | |
95 | | /* Backend-independent JOSE names and values. Key types match cjose and are compile-time asserted. */ |
96 | 0 | #define OIDC_JOSE_HDR_ALG "alg" |
97 | 0 | #define OIDC_JOSE_HDR_ENC "enc" |
98 | 0 | #define OIDC_JOSE_HDR_KID "kid" |
99 | 0 | #define OIDC_JOSE_HDR_ALG_DIR "dir" |
100 | 0 | #define OIDC_JOSE_HDR_ALG_RS256 "RS256" |
101 | 0 | #define OIDC_JOSE_HDR_ALG_PS256 "PS256" |
102 | 0 | #define OIDC_JOSE_HDR_ALG_HS256 "HS256" |
103 | 0 | #define OIDC_JOSE_HDR_ALG_ES256 "ES256" |
104 | 0 | #define OIDC_JOSE_HDR_ALG_ES384 "ES384" |
105 | 0 | #define OIDC_JOSE_HDR_ALG_ES512 "ES512" |
106 | 0 | #define OIDC_JOSE_HDR_ENC_A256GCM "A256GCM" |
107 | 0 | #define OIDC_JOSE_HDR_ENC_A128CBC_HS256 "A128CBC-HS256" |
108 | | |
109 | 0 | #define OIDC_JOSE_JWK_KTY_RSA 1 |
110 | 0 | #define OIDC_JOSE_JWK_KTY_EC 2 |
111 | 0 | #define OIDC_JOSE_JWK_KTY_OCT 3 |
112 | | |
113 | | /* struct for returning errors to the caller */ |
114 | | typedef struct { |
115 | | char source[OIDC_JOSE_ERROR_SOURCE_LENGTH]; |
116 | | int line; |
117 | | char function[OIDC_JOSE_ERROR_FUNCTION_LENGTH]; |
118 | | char text[OIDC_JOSE_ERROR_TEXT_LENGTH]; |
119 | | } oidc_jose_error_t; |
120 | | |
121 | | /* |
122 | | * error handling functions |
123 | | */ |
124 | 108k | #define oidc_jose_error(err, msg, ...) _oidc_jose_error_set(err, __FILE__, __LINE__, __FUNCTION__, msg, ##__VA_ARGS__) |
125 | | #define oidc_jose_error_openssl(err, msg, ...) \ |
126 | 0 | _oidc_jose_error_set(err, __FILE__, __LINE__, __FUNCTION__, "%s() failed: %s", msg, \ |
127 | 0 | ERR_error_string(ERR_get_error(), NULL), ##__VA_ARGS__) |
128 | 1 | #define oidc_jose_e2s(pool, err) apr_psprintf(pool, "[%s:%d: %s]: %s", err.source, err.line, err.function, err.text) |
129 | | |
130 | | /* |
131 | | * helper functions |
132 | | */ |
133 | | |
134 | | /* helpers to find out about the supported ala/enc algorithms */ |
135 | | apr_array_header_t *oidc_jose_jws_supported_algorithms(apr_pool_t *pool); |
136 | | apr_byte_t oidc_jose_jws_algorithm_is_supported(apr_pool_t *pool, const char *alg); |
137 | | apr_array_header_t *oidc_jose_jwe_supported_algorithms(apr_pool_t *pool); |
138 | | apr_byte_t oidc_jose_jwe_algorithm_is_supported(apr_pool_t *pool, const char *alg); |
139 | | apr_array_header_t *oidc_jose_jwe_supported_encryptions(apr_pool_t *pool); |
140 | | apr_byte_t oidc_jose_jwe_encryption_is_supported(apr_pool_t *pool, const char *enc); |
141 | | |
142 | | /* return the version string of the underlying JOSE backend library */ |
143 | | const char *oidc_jose_version(void); |
144 | | |
145 | | /* hash helpers */ |
146 | | apr_byte_t oidc_jose_hash_string(apr_pool_t *pool, const char *alg, const char *msg, char **hash, |
147 | | unsigned int *hash_len, oidc_jose_error_t *err); |
148 | | int oidc_jose_hash_length(const char *alg); |
149 | | apr_byte_t oidc_jose_hash_bytes(apr_pool_t *pool, const char *s_digest, const unsigned char *input, |
150 | | unsigned int input_len, unsigned char **output, unsigned int *output_len, |
151 | | oidc_jose_error_t *err); |
152 | | apr_byte_t oidc_jose_hash_and_base64url_encode(apr_pool_t *pool, const char *openssl_hash_algo, const char *input, |
153 | | int input_len, char **output, oidc_jose_error_t *err); |
154 | | |
155 | | /* return a string claim value from a JSON object */ |
156 | | apr_byte_t oidc_jose_get_string(apr_pool_t *pool, const oidc_json_t *json, const char *claim_name, |
157 | | apr_byte_t is_mandatory, char **result, oidc_jose_error_t *err); |
158 | | apr_byte_t oidc_jose_get_timestamp(const oidc_json_t *json, const char *claim_name, apr_byte_t is_mandatory, |
159 | | double *result, oidc_jose_error_t *err); |
160 | | |
161 | | apr_byte_t oidc_jose_compress(apr_pool_t *pool, const char *input, int input_len, char **output, int *output_len, |
162 | | oidc_jose_error_t *err); |
163 | | apr_byte_t oidc_jose_uncompress(apr_pool_t *pool, const char *input, int input_len, char **output, int *output_len, |
164 | | oidc_jose_error_t *err); |
165 | | |
166 | | /* a parsed JWK/JWT JSON object */ |
167 | | typedef struct oidc_jose_json_t { |
168 | | /* parsed JSON struct representation */ |
169 | | oidc_json_t *json; |
170 | | /* string representation */ |
171 | | char *str; |
172 | | } oidc_jose_json_t; |
173 | | |
174 | | /* |
175 | | * JSON Web Key handling |
176 | | */ |
177 | | |
178 | | /* parsed JWK */ |
179 | | typedef struct oidc_jwk_t { |
180 | | /* use type */ |
181 | | char *use; |
182 | | /* JWK "alg" (algorithm) parameter (optional; RFC 7517 section 4.4); when set it is published in the |
183 | | * JWKs so an OP can select this key for the named algorithm, and it drives per-alg key duplication */ |
184 | | char *alg; |
185 | | /* key type */ |
186 | | int kty; |
187 | | /* key identifier */ |
188 | | char *kid; |
189 | | /* X.509 Certificate Chain */ |
190 | | apr_array_header_t *x5c; |
191 | | /* X.509 Certificate SHA-1 Thumbprint */ |
192 | | char *x5t; |
193 | | /* X.509 Certificate SHA-256 Thumbprint */ |
194 | | char *x5t_S256; |
195 | | /* backend-private handle to the JOSE library's key object (currently a cjose_jwk_t); typed as |
196 | | * void* so this public header needs no backend types: only jose.c and the jose/ subdirectory, |
197 | | * which include the real backend header, may dereference or operate on it */ |
198 | | void *cjose_jwk; |
199 | | } oidc_jwk_t; |
200 | | |
201 | | /* decrypt a JWT */ |
202 | | apr_byte_t oidc_jwe_decrypt(apr_pool_t *pool, const char *input_json, apr_hash_t *keys, char **plaintext, |
203 | | int *plaintext_len, oidc_jose_error_t *err, apr_byte_t import_must_succeed); |
204 | | /* parse a JSON string (JWK) to a JWK struct */ |
205 | | oidc_jwk_t *oidc_jwk_parse(apr_pool_t *pool, const oidc_json_t *json, oidc_jose_error_t *err); |
206 | | oidc_jwk_t *oidc_jwk_copy(apr_pool_t *pool, const oidc_jwk_t *jwk); |
207 | | /* parse a JSON object (JWK) in to a JWK struct */ |
208 | | apr_byte_t oidc_jwk_parse_json(apr_pool_t *pool, const oidc_json_t *json, oidc_jwk_t **jwk, oidc_jose_error_t *err); |
209 | | /* parse a JSON object (JWKS) to a list of JWK structs */ |
210 | | apr_byte_t oidc_jwks_parse_json(apr_pool_t *pool, const oidc_json_t *json, apr_array_header_t **jwk_list, |
211 | | oidc_jose_error_t *err); |
212 | | /* test if JSON object looks like JWK */ |
213 | | apr_byte_t oidc_is_jwk(const oidc_json_t *json); |
214 | | /* test if JSON object looks like JWKS */ |
215 | | apr_byte_t oidc_is_jwks(const oidc_json_t *json); |
216 | | /* convert a JWK struct to a JSON string */ |
217 | | apr_byte_t oidc_jwk_to_json(apr_pool_t *pool, const oidc_jwk_t *jwk, char **s_json, oidc_jose_error_t *err); |
218 | | /* convert the PUBLIC part of a JWK struct to a JSON string (excludes private key material) */ |
219 | | 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); |
220 | | /* derive the default JWS signing algorithm for a key (RS256 for RSA; ES256/384/512 per EC curve); NULL if unsupported |
221 | | */ |
222 | | const char *oidc_jwk_default_jws_alg(const oidc_jwk_t *jwk); |
223 | | /* destroy resources allocated for a JWK struct */ |
224 | | void oidc_jwk_destroy(oidc_jwk_t *jwk); |
225 | | /* destroy a list of JWKs structs */ |
226 | | void oidc_jwk_list_destroy_hash(apr_hash_t *key); |
227 | | apr_array_header_t *oidc_jwk_list_copy(apr_pool_t *pool, apr_array_header_t *src); |
228 | | void oidc_jwk_list_destroy(apr_array_header_t *keys_list); |
229 | | /* create an "oct" symmetric JWK */ |
230 | | oidc_jwk_t *oidc_jwk_create_symmetric_key(apr_pool_t *pool, const char *kid, const unsigned char *key, |
231 | | unsigned int key_len, apr_byte_t set_kid, oidc_jose_error_t *err); |
232 | | |
233 | | /* parse an X.509 PEM formatted certificate file with a public key to a JWK struct */ |
234 | | apr_byte_t oidc_jwk_parse_pem_public_key(apr_pool_t *pool, const char *kid, const char *filename, oidc_jwk_t **jwk, |
235 | | oidc_jose_error_t *err); |
236 | | /* parse an X.509 PEM formatted private key file to a JWK */ |
237 | | apr_byte_t oidc_jwk_parse_pem_private_key(apr_pool_t *pool, const char *kid, const char *filename, oidc_jwk_t **jwk, |
238 | | oidc_jose_error_t *err); |
239 | | |
240 | | /* |
241 | | * JSON Web Token handling |
242 | | */ |
243 | | |
244 | | /* represents NULL timestamp */ |
245 | 0 | #define OIDC_JWT_CLAIM_TIME_EMPTY -1 |
246 | | |
247 | | /* a parsed JWT header */ |
248 | | typedef struct oidc_jwt_hdr_t { |
249 | | /* parsed header value */ |
250 | | oidc_jose_json_t value; |
251 | | /* JWT "alg" claim value; signing algorithm */ |
252 | | char *alg; |
253 | | /* JWT "kid" claim value; key identifier */ |
254 | | char *kid; |
255 | | /* JWT "enc" claim value; encryption algorithm */ |
256 | | char *enc; |
257 | | /* JWT "cty" claim value; content type (e.g. "JWT" for a Nested JWT) */ |
258 | | char *cty; |
259 | | /* JWT "x5t" thumbprint */ |
260 | | char *x5t; |
261 | | } oidc_jwt_hdr_t; |
262 | | |
263 | | /* parsed JWT payload */ |
264 | | typedef struct oidc_jwt_payload_t { |
265 | | /* parsed payload value */ |
266 | | oidc_jose_json_t value; |
267 | | /* JWT "iss" claim value; JWT issuer */ |
268 | | char *iss; |
269 | | /* JWT "sub" claim value; subject/principal */ |
270 | | char *sub; |
271 | | /* parsed JWT "exp" claim value; token expiry */ |
272 | | double exp; |
273 | | /* parsed JWT "iat" claim value; issued-at timestamp */ |
274 | | double iat; |
275 | | } oidc_jwt_payload_t; |
276 | | |
277 | | /* parsed JWT */ |
278 | | typedef struct oidc_jwt_t { |
279 | | /* parsed JWT header */ |
280 | | oidc_jwt_hdr_t header; |
281 | | /* parsed JWT payload */ |
282 | | oidc_jwt_payload_t payload; |
283 | | /* backend-private handle to the JOSE library's signature object (currently a cjose_jws_t); typed |
284 | | * as void* so this public header needs no backend types: only jose.c and the jose/ subdirectory, |
285 | | * which include the real backend header, may dereference or operate on it */ |
286 | | void *cjose_jws; |
287 | | } oidc_jwt_t; |
288 | | |
289 | | /* parse a string into a JSON Web Token struct and (optionally) decrypt it */ |
290 | | apr_byte_t oidc_jwt_parse(apr_pool_t *pool, const char *s_json, oidc_jwt_t **j_jwt, apr_hash_t *keys, |
291 | | apr_byte_t compress, oidc_jose_error_t *err); |
292 | | /* sign a JWT with a JWK */ |
293 | | apr_byte_t oidc_jwt_sign(apr_pool_t *pool, oidc_jwt_t *jwt, const oidc_jwk_t *jwk, apr_byte_t compress, |
294 | | oidc_jose_error_t *err); |
295 | | /* verify a JWT a key in a list of JWKs */ |
296 | | apr_byte_t oidc_jwt_verify(apr_pool_t *pool, oidc_jwt_t *jwt, apr_hash_t *keys, oidc_jose_error_t *err); |
297 | | /* perform compact serialization on a JWT and return the resulting string */ |
298 | | char *oidc_jose_jwt_serialize(apr_pool_t *pool, oidc_jwt_t *jwt, oidc_jose_error_t *err); |
299 | | /* encrypt JWT */ |
300 | | apr_byte_t oidc_jwt_encrypt(apr_pool_t *pool, oidc_jwt_t *jwe, const oidc_jwk_t *jwk, const char *payload, |
301 | | int payload_len, char **serialized, oidc_jose_error_t *err); |
302 | | |
303 | | /* create a new JWT */ |
304 | | oidc_jwt_t *oidc_jwt_new(apr_pool_t *pool, int create_header, int create_payload); |
305 | | /* destroy resources allocated for JWT */ |
306 | | void oidc_jwt_destroy(oidc_jwt_t *); |
307 | | |
308 | | /* get a header value from a JWT */ |
309 | | const char *oidc_jwt_hdr_get(oidc_jwt_t *jwt, const char *key); |
310 | | /* set a JWT header member to a raw (pre-serialized) JSON value */ |
311 | | apr_byte_t oidc_jwt_hdr_set_json(oidc_jwt_t *jwt, const char *key, const char *raw_json, oidc_jose_error_t *err); |
312 | | /* return the key type of a JWT */ |
313 | | int oidc_jwt_alg2kty(const oidc_jwt_t *jwt); |
314 | | /* return the key type (cjose kty) that the provided JWA algorithm name requires, or -1 if unknown */ |
315 | | int oidc_alg2kty(const char *alg); |
316 | | /* return the key size for an algorithm */ |
317 | | unsigned int oidc_alg2keysize(const char *alg); |
318 | | |
319 | | apr_byte_t oidc_jwk_pem_bio_to_jwk(apr_pool_t *pool, BIO *input, const char *kid, oidc_jwk_t **jwk, |
320 | | apr_byte_t is_private_key, oidc_jose_error_t *err); |
321 | | |
322 | | #endif /* _MOD_AUTH_OPENIDC_JOSE_H_ */ |