1 /*!
2 * Copyrights
3 *
4 * Portions created or assigned to Cisco Systems, Inc. are
5 * Copyright (c) 2014-2016 Cisco Systems, Inc. All Rights Reserved.
6 */
7
8 #include <cjose/base64.h>
9 #include <cjose/header.h>
10 #include <cjose/jws.h>
11 #include <cjose/jwk.h>
12 #include <cjose/util.h>
13
14 #include <string.h>
15 #include <assert.h>
16 #include <openssl/evp.h>
17 #include <openssl/rsa.h>
18 #include <openssl/err.h>
19 #include <openssl/hmac.h>
20
21 #include "include/jwk_int.h"
22 #include "include/header_int.h"
23 #include "include/jws_int.h"
24
25 ////////////////////////////////////////////////////////////////////////////////
26 static bool _cjose_jws_build_dig_sha(cjose_jws_t *jws, const cjose_jwk_t *jwk, cjose_err *err);
27
28 static bool _cjose_jws_build_sig_ps(cjose_jws_t *jws, const cjose_jwk_t *jwk, cjose_err *err);
29
30 static bool _cjose_jws_build_dig_hmac_sha(cjose_jws_t *jws, const cjose_jwk_t *jwk, cjose_err *err);
31
32 static bool _cjose_jws_verify_sig_ps(cjose_jws_t *jws, const cjose_jwk_t *jwk, cjose_err *err);
33
34 static bool _cjose_jws_build_sig_rs(cjose_jws_t *jws, const cjose_jwk_t *jwk, cjose_err *err);
35
36 static bool _cjose_jws_verify_sig_rs(cjose_jws_t *jws, const cjose_jwk_t *jwk, cjose_err *err);
37
38 static bool _cjose_jws_build_sig_hmac_sha(cjose_jws_t *jws, const cjose_jwk_t *jwk, cjose_err *err);
39
40 static bool _cjose_jws_verify_sig_hmac_sha(cjose_jws_t *jws, const cjose_jwk_t *jwk, cjose_err *err);
41
42 static bool _cjose_jws_build_sig_ec(cjose_jws_t *jws, const cjose_jwk_t *jwk, cjose_err *err);
43
44 static bool _cjose_jws_verify_sig_ec(cjose_jws_t *jws, const cjose_jwk_t *jwk, cjose_err *err);
45
46 ////////////////////////////////////////////////////////////////////////////////
_cjose_jws_build_hdr(cjose_jws_t * jws,cjose_header_t * header,cjose_err * err)47 static bool _cjose_jws_build_hdr(cjose_jws_t *jws, cjose_header_t *header, cjose_err *err)
48 {
49 // save header object as part of the JWS (and incr. refcount)
50 jws->hdr = (json_t *)header;
51 json_incref(jws->hdr);
52
53 // base64url encode the header
54 char *hdr_str = json_dumps(jws->hdr, JSON_ENCODE_ANY | JSON_PRESERVE_ORDER);
55 if (NULL == hdr_str)
56 {
57 CJOSE_ERROR(err, CJOSE_ERR_NO_MEMORY);
58 return false;
59 }
60 if (!cjose_base64url_encode((const uint8_t *)hdr_str, strlen(hdr_str), &jws->hdr_b64u, &jws->hdr_b64u_len, err))
61 {
62 free(hdr_str);
63 return false;
64 }
65 free(hdr_str);
66
67 return true;
68 }
69
70 ////////////////////////////////////////////////////////////////////////////////
_cjose_jws_validate_hdr(cjose_jws_t * jws,cjose_err * err)71 static bool _cjose_jws_validate_hdr(cjose_jws_t *jws, cjose_err *err)
72 {
73 // make sure we have an alg header
74 json_t *alg_obj = json_object_get(jws->hdr, CJOSE_HDR_ALG);
75 if ((NULL == alg_obj) || (!json_is_string(alg_obj)))
76 {
77 CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG);
78 return false;
79 }
80 const char *alg = json_string_value(alg_obj);
81
82 if ((strcmp(alg, CJOSE_HDR_ALG_PS256) == 0) || (strcmp(alg, CJOSE_HDR_ALG_PS384) == 0)
83 || (strcmp(alg, CJOSE_HDR_ALG_PS512) == 0))
84 {
85 jws->fns.digest = _cjose_jws_build_dig_sha;
86 jws->fns.sign = _cjose_jws_build_sig_ps;
87 jws->fns.verify = _cjose_jws_verify_sig_ps;
88 }
89 else if ((strcmp(alg, CJOSE_HDR_ALG_RS256) == 0) || (strcmp(alg, CJOSE_HDR_ALG_RS384) == 0)
90 || (strcmp(alg, CJOSE_HDR_ALG_RS512) == 0))
91 {
92 jws->fns.digest = _cjose_jws_build_dig_sha;
93 jws->fns.sign = _cjose_jws_build_sig_rs;
94 jws->fns.verify = _cjose_jws_verify_sig_rs;
95 }
96 else if ((strcmp(alg, CJOSE_HDR_ALG_HS256) == 0) || (strcmp(alg, CJOSE_HDR_ALG_HS384) == 0)
97 || (strcmp(alg, CJOSE_HDR_ALG_HS512) == 0))
98 {
99 jws->fns.digest = _cjose_jws_build_dig_hmac_sha;
100 jws->fns.sign = _cjose_jws_build_sig_hmac_sha;
101 jws->fns.verify = _cjose_jws_verify_sig_hmac_sha;
102 }
103 else if ((strcmp(alg, CJOSE_HDR_ALG_ES256) == 0) || (strcmp(alg, CJOSE_HDR_ALG_ES384) == 0)
104 || (strcmp(alg, CJOSE_HDR_ALG_ES512) == 0))
105 {
106 jws->fns.digest = _cjose_jws_build_dig_sha;
107 jws->fns.sign = _cjose_jws_build_sig_ec;
108 jws->fns.verify = _cjose_jws_verify_sig_ec;
109 }
110 else
111 {
112 CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG);
113 return false;
114 }
115
116 return true;
117 }
118
119 ////////////////////////////////////////////////////////////////////////////////
_cjose_jws_build_dat(cjose_jws_t * jws,const uint8_t * plaintext,size_t plaintext_len,cjose_err * err)120 static bool _cjose_jws_build_dat(cjose_jws_t *jws, const uint8_t *plaintext, size_t plaintext_len, cjose_err *err)
121 {
122 // copy plaintext data
123 jws->dat_len = plaintext_len;
124 jws->dat = (uint8_t *)cjose_get_alloc()(jws->dat_len);
125 if (NULL == jws->dat)
126 {
127 CJOSE_ERROR(err, CJOSE_ERR_NO_MEMORY);
128 return false;
129 }
130 memcpy(jws->dat, plaintext, jws->dat_len);
131
132 // base64url encode data
133 if (!cjose_base64url_encode((const uint8_t *)plaintext, plaintext_len, &jws->dat_b64u, &jws->dat_b64u_len, err))
134 {
135 return false;
136 }
137
138 return true;
139 }
140
141 ////////////////////////////////////////////////////////////////////////////////
_cjose_jws_build_dig_sha(cjose_jws_t * jws,const cjose_jwk_t * jwk,cjose_err * err)142 static bool _cjose_jws_build_dig_sha(cjose_jws_t *jws, const cjose_jwk_t *jwk, cjose_err *err)
143 {
144 bool retval = false;
145 EVP_MD_CTX *ctx = NULL;
146
147 // make sure we have an alg header
148 json_t *alg_obj = json_object_get(jws->hdr, CJOSE_HDR_ALG);
149 if (NULL == alg_obj)
150 {
151 CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG);
152 return false;
153 }
154 const char *alg = json_string_value(alg_obj);
155
156 // build digest using SHA-256/384/512 digest algorithm
157 const EVP_MD *digest_alg = NULL;
158 if ((strcmp(alg, CJOSE_HDR_ALG_RS256) == 0) || (strcmp(alg, CJOSE_HDR_ALG_PS256) == 0)
159 || (strcmp(alg, CJOSE_HDR_ALG_ES256) == 0))
160 digest_alg = EVP_sha256();
161 else if ((strcmp(alg, CJOSE_HDR_ALG_RS384) == 0) || (strcmp(alg, CJOSE_HDR_ALG_PS384) == 0)
162 || (strcmp(alg, CJOSE_HDR_ALG_ES384) == 0))
163 digest_alg = EVP_sha384();
164 else if ((strcmp(alg, CJOSE_HDR_ALG_RS512) == 0) || (strcmp(alg, CJOSE_HDR_ALG_PS512) == 0)
165 || (strcmp(alg, CJOSE_HDR_ALG_ES512) == 0))
166 digest_alg = EVP_sha512();
167
168 if (NULL == digest_alg)
169 {
170 CJOSE_ERROR(err, CJOSE_ERR_CRYPTO);
171 goto _cjose_jws_build_dig_sha_cleanup;
172 }
173
174 // allocate buffer for digest
175 jws->dig_len = EVP_MD_size(digest_alg);
176 jws->dig = (uint8_t *)cjose_get_alloc()(jws->dig_len);
177 if (NULL == jws->dig)
178 {
179 CJOSE_ERROR(err, CJOSE_ERR_NO_MEMORY);
180 goto _cjose_jws_build_dig_sha_cleanup;
181 }
182
183 // instantiate and initialize a new mac digest context
184 ctx = EVP_MD_CTX_create();
185 if (NULL == ctx)
186 {
187 CJOSE_ERROR(err, CJOSE_ERR_CRYPTO);
188 goto _cjose_jws_build_dig_sha_cleanup;
189 }
190 EVP_MD_CTX_init(ctx);
191
192 // create digest as DIGEST(B64U(HEADER).B64U(DATA))
193 if (EVP_DigestInit_ex(ctx, digest_alg, NULL) != 1)
194 {
195 CJOSE_ERROR(err, CJOSE_ERR_CRYPTO);
196 goto _cjose_jws_build_dig_sha_cleanup;
197 }
198 if (EVP_DigestUpdate(ctx, jws->hdr_b64u, jws->hdr_b64u_len) != 1)
199 {
200 CJOSE_ERROR(err, CJOSE_ERR_CRYPTO);
201 goto _cjose_jws_build_dig_sha_cleanup;
202 }
203 if (EVP_DigestUpdate(ctx, ".", 1) != 1)
204 {
205 CJOSE_ERROR(err, CJOSE_ERR_CRYPTO);
206 goto _cjose_jws_build_dig_sha_cleanup;
207 }
208 if (EVP_DigestUpdate(ctx, jws->dat_b64u, jws->dat_b64u_len) != 1)
209 {
210 CJOSE_ERROR(err, CJOSE_ERR_CRYPTO);
211 goto _cjose_jws_build_dig_sha_cleanup;
212 }
213 if (EVP_DigestFinal_ex(ctx, jws->dig, NULL) != 1)
214 {
215 CJOSE_ERROR(err, CJOSE_ERR_CRYPTO);
216 goto _cjose_jws_build_dig_sha_cleanup;
217 }
218
219 // if we got this far - success
220 retval = true;
221
222 _cjose_jws_build_dig_sha_cleanup:
223 if (NULL != ctx)
224 {
225 EVP_MD_CTX_destroy(ctx);
226 }
227
228 return retval;
229 }
230
231 ////////////////////////////////////////////////////////////////////////////////
_cjose_jws_build_dig_hmac_sha(cjose_jws_t * jws,const cjose_jwk_t * jwk,cjose_err * err)232 static bool _cjose_jws_build_dig_hmac_sha(cjose_jws_t *jws, const cjose_jwk_t *jwk, cjose_err *err)
233 {
234 bool retval = false;
235 HMAC_CTX *ctx = NULL;
236
237 // make sure we have an alg header
238 json_t *alg_obj = json_object_get(jws->hdr, CJOSE_HDR_ALG);
239 if (NULL == alg_obj)
240 {
241 CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG);
242 return false;
243 }
244 const char *alg = json_string_value(alg_obj);
245
246 // build digest using SHA-256/384/512 digest algorithm
247 const EVP_MD *digest_alg = NULL;
248 if (strcmp(alg, CJOSE_HDR_ALG_HS256) == 0)
249 digest_alg = EVP_sha256();
250 else if (strcmp(alg, CJOSE_HDR_ALG_HS384) == 0)
251 digest_alg = EVP_sha384();
252 else if (strcmp(alg, CJOSE_HDR_ALG_HS512) == 0)
253 digest_alg = EVP_sha512();
254
255 if (NULL == digest_alg)
256 {
257 CJOSE_ERROR(err, CJOSE_ERR_CRYPTO);
258 goto _cjose_jws_build_dig_hmac_sha_cleanup;
259 }
260
261 // allocate buffer for digest
262 jws->dig_len = EVP_MD_size(digest_alg);
263 jws->dig = (uint8_t *)cjose_get_alloc()(jws->dig_len);
264 if (NULL == jws->dig)
265 {
266 CJOSE_ERROR(err, CJOSE_ERR_NO_MEMORY);
267 goto _cjose_jws_build_dig_hmac_sha_cleanup;
268 }
269
270 // instantiate and initialize a new mac digest context
271 #if defined(CJOSE_OPENSSL_11X)
272 ctx = HMAC_CTX_new();
273 #else
274 ctx = cjose_get_alloc()(sizeof(HMAC_CTX));
275 #endif
276 if (NULL == ctx)
277 {
278 CJOSE_ERROR(err, CJOSE_ERR_NO_MEMORY);
279 goto _cjose_jws_build_dig_hmac_sha_cleanup;
280 }
281
282 #if !defined(CJOSE_OPENSSL_11X)
283 HMAC_CTX_init(ctx);
284 #endif
285
286 // create digest as DIGEST(B64U(HEADER).B64U(DATA))
287 if (HMAC_Init_ex(ctx, jwk->keydata, jwk->keysize / 8, digest_alg, NULL) != 1)
288 {
289 CJOSE_ERROR(err, CJOSE_ERR_CRYPTO);
290 goto _cjose_jws_build_dig_hmac_sha_cleanup;
291 }
292 if (HMAC_Update(ctx, (const unsigned char *)jws->hdr_b64u, jws->hdr_b64u_len) != 1)
293 {
294 CJOSE_ERROR(err, CJOSE_ERR_CRYPTO);
295 goto _cjose_jws_build_dig_hmac_sha_cleanup;
296 }
297 if (HMAC_Update(ctx, (const unsigned char *)".", 1) != 1)
298 {
299 CJOSE_ERROR(err, CJOSE_ERR_CRYPTO);
300 goto _cjose_jws_build_dig_hmac_sha_cleanup;
301 }
302 if (HMAC_Update(ctx, (const unsigned char *)jws->dat_b64u, jws->dat_b64u_len) != 1)
303 {
304 CJOSE_ERROR(err, CJOSE_ERR_CRYPTO);
305 goto _cjose_jws_build_dig_hmac_sha_cleanup;
306 }
307 if (HMAC_Final(ctx, jws->dig, NULL) != 1)
308 {
309 CJOSE_ERROR(err, CJOSE_ERR_CRYPTO);
310 goto _cjose_jws_build_dig_hmac_sha_cleanup;
311 }
312
313 // if we got this far - success
314 retval = true;
315
316 _cjose_jws_build_dig_hmac_sha_cleanup:
317 if (NULL != ctx)
318 {
319 #if defined(CJOSE_OPENSSL_11X)
320 HMAC_CTX_free(ctx);
321 #else
322 HMAC_CTX_cleanup(ctx);
323 cjose_get_dealloc()(ctx);
324 #endif
325 }
326
327 return retval;
328 }
329
330 ////////////////////////////////////////////////////////////////////////////////
_cjose_jws_build_sig_ps(cjose_jws_t * jws,const cjose_jwk_t * jwk,cjose_err * err)331 static bool _cjose_jws_build_sig_ps(cjose_jws_t *jws, const cjose_jwk_t *jwk, cjose_err *err)
332 {
333 bool retval = false;
334 uint8_t *em = NULL;
335 size_t em_len = 0;
336
337 // ensure jwk is private RSA
338 if (jwk->kty != CJOSE_JWK_KTY_RSA)
339 {
340 CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG);
341 goto _cjose_jws_build_sig_ps_cleanup;
342 }
343 RSA *rsa = (RSA *)jwk->keydata;
344 BIGNUM *rsa_n = NULL, *rsa_e = NULL, *rsa_d = NULL;
345 _cjose_jwk_rsa_get(rsa, &rsa_n, &rsa_e, &rsa_d);
346 if (!rsa || !rsa_e || !rsa_n || !rsa_d)
347 {
348 CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG);
349 return false;
350 }
351
352 // make sure we have an alg header
353 json_t *alg_obj = json_object_get(jws->hdr, CJOSE_HDR_ALG);
354 if (NULL == alg_obj)
355 {
356 CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG);
357 return false;
358 }
359 const char *alg = json_string_value(alg_obj);
360
361 // build digest using SHA-256/384/512 digest algorithm
362 const EVP_MD *digest_alg = NULL;
363 if (strcmp(alg, CJOSE_HDR_ALG_PS256) == 0)
364 digest_alg = EVP_sha256();
365 else if (strcmp(alg, CJOSE_HDR_ALG_PS384) == 0)
366 digest_alg = EVP_sha384();
367 else if (strcmp(alg, CJOSE_HDR_ALG_PS512) == 0)
368 digest_alg = EVP_sha512();
369
370 if (NULL == digest_alg)
371 {
372 CJOSE_ERROR(err, CJOSE_ERR_CRYPTO);
373 goto _cjose_jws_build_sig_ps_cleanup;
374 }
375
376 // apply EMSA-PSS encoding (RFC-3447, 8.1.1, step 1)
377 // (RSA_padding_add_PKCS1_PSS includes PKCS1_MGF1, -1 => saltlen = hashlen)
378 em_len = RSA_size((RSA *)jwk->keydata);
379 em = (uint8_t *)cjose_get_alloc()(em_len);
380 if (NULL == em)
381 {
382 CJOSE_ERROR(err, CJOSE_ERR_NO_MEMORY);
383 goto _cjose_jws_build_sig_ps_cleanup;
384 }
385 if (RSA_padding_add_PKCS1_PSS((RSA *)jwk->keydata, em, jws->dig, digest_alg, -1) != 1)
386 {
387 CJOSE_ERROR(err, CJOSE_ERR_CRYPTO);
388 goto _cjose_jws_build_sig_ps_cleanup;
389 }
390
391 // sign the digest (RFC-3447, 8.1.1, step 2)
392 jws->sig_len = em_len;
393 jws->sig = (uint8_t *)cjose_get_alloc()(jws->sig_len);
394 if (NULL == jws->sig)
395 {
396 CJOSE_ERROR(err, CJOSE_ERR_NO_MEMORY);
397 goto _cjose_jws_build_sig_ps_cleanup;
398 }
399
400 if (RSA_private_encrypt(em_len, em, jws->sig, (RSA *)jwk->keydata, RSA_NO_PADDING) != jws->sig_len)
401 {
402 CJOSE_ERROR(err, CJOSE_ERR_CRYPTO);
403 goto _cjose_jws_build_sig_ps_cleanup;
404 }
405
406 // base64url encode signed digest
407 if (!cjose_base64url_encode((const uint8_t *)jws->sig, jws->sig_len, &jws->sig_b64u, &jws->sig_b64u_len, err))
408 {
409 goto _cjose_jws_build_sig_ps_cleanup;
410 }
411
412 // if we got this far - success
413 retval = true;
414
415 _cjose_jws_build_sig_ps_cleanup:
416 cjose_get_dealloc()(em);
417
418 return retval;
419 }
420
421 ////////////////////////////////////////////////////////////////////////////////
_cjose_jws_build_sig_rs(cjose_jws_t * jws,const cjose_jwk_t * jwk,cjose_err * err)422 static bool _cjose_jws_build_sig_rs(cjose_jws_t *jws, const cjose_jwk_t *jwk, cjose_err *err)
423 {
424 // ensure jwk is private RSA
425 if (jwk->kty != CJOSE_JWK_KTY_RSA)
426 {
427 CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG);
428 return false;
429 }
430 RSA *rsa = (RSA *)jwk->keydata;
431 BIGNUM *rsa_n = NULL, *rsa_e = NULL, *rsa_d = NULL;
432 _cjose_jwk_rsa_get(rsa, &rsa_n, &rsa_e, &rsa_d);
433 if (!rsa || !rsa_e || !rsa_n || !rsa_d)
434 {
435 CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG);
436 return false;
437 }
438
439 // allocate buffer for signature
440 jws->sig_len = RSA_size((RSA *)jwk->keydata);
441 jws->sig = (uint8_t *)cjose_get_alloc()(jws->sig_len);
442 if (NULL == jws->sig)
443 {
444 CJOSE_ERROR(err, CJOSE_ERR_NO_MEMORY);
445 return false;
446 }
447
448 // make sure we have an alg header
449 json_t *alg_obj = json_object_get(jws->hdr, CJOSE_HDR_ALG);
450 if (NULL == alg_obj)
451 {
452 CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG);
453 return false;
454 }
455 const char *alg = json_string_value(alg_obj);
456
457 // build digest using SHA-256/384/512 digest algorithm
458 int digest_alg = -1;
459 if (strcmp(alg, CJOSE_HDR_ALG_RS256) == 0)
460 digest_alg = NID_sha256;
461 else if (strcmp(alg, CJOSE_HDR_ALG_RS384) == 0)
462 digest_alg = NID_sha384;
463 else if (strcmp(alg, CJOSE_HDR_ALG_RS512) == 0)
464 digest_alg = NID_sha512;
465 if (-1 == digest_alg)
466 {
467 CJOSE_ERROR(err, CJOSE_ERR_CRYPTO);
468 return false;
469 }
470
471 unsigned int siglen;
472 if (RSA_sign(digest_alg, jws->dig, jws->dig_len, jws->sig, &siglen, (RSA *)jwk->keydata) != 1)
473 {
474 CJOSE_ERROR(err, CJOSE_ERR_CRYPTO);
475 return false;
476 }
477 jws->sig_len = siglen;
478
479 // base64url encode signed digest
480 if (!cjose_base64url_encode((const uint8_t *)jws->sig, jws->sig_len, &jws->sig_b64u, &jws->sig_b64u_len, err))
481 {
482 CJOSE_ERROR(err, CJOSE_ERR_CRYPTO);
483 return false;
484 }
485
486 return true;
487 }
488
_cjose_jws_build_sig_hmac_sha(cjose_jws_t * jws,const cjose_jwk_t * jwk,cjose_err * err)489 static bool _cjose_jws_build_sig_hmac_sha(cjose_jws_t *jws, const cjose_jwk_t *jwk, cjose_err *err)
490 {
491 // ensure jwk is OCT
492 if (jwk->kty != CJOSE_JWK_KTY_OCT)
493 {
494 CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG);
495 return false;
496 }
497
498 // allocate buffer for signature
499 jws->sig_len = jws->dig_len;
500 jws->sig = (uint8_t *)cjose_get_alloc()(jws->sig_len);
501 if (NULL == jws->sig)
502 {
503 CJOSE_ERROR(err, CJOSE_ERR_NO_MEMORY);
504 return false;
505 }
506
507 memcpy(jws->sig, jws->dig, jws->sig_len);
508
509 // base64url encode signed digest
510 if (!cjose_base64url_encode((const uint8_t *)jws->sig, jws->sig_len, &jws->sig_b64u, &jws->sig_b64u_len, err))
511 {
512 CJOSE_ERROR(err, CJOSE_ERR_CRYPTO);
513 return false;
514 }
515
516 return true;
517 }
518
519 ////////////////////////////////////////////////////////////////////////////////
_cjose_jws_build_sig_ec(cjose_jws_t * jws,const cjose_jwk_t * jwk,cjose_err * err)520 static bool _cjose_jws_build_sig_ec(cjose_jws_t *jws, const cjose_jwk_t *jwk, cjose_err *err)
521 {
522 bool retval = false;
523
524 // ensure jwk is EC
525 if (jwk->kty != CJOSE_JWK_KTY_EC)
526 {
527 CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG);
528 return false;
529 }
530
531 ec_keydata *keydata = (ec_keydata *)jwk->keydata;
532 EC_KEY *ec = keydata->key;
533
534 ECDSA_SIG *ecdsa_sig = ECDSA_do_sign(jws->dig, jws->dig_len, ec);
535 if (NULL == ecdsa_sig)
536 {
537 CJOSE_ERROR(err, CJOSE_ERR_CRYPTO);
538 goto _cjose_jws_build_sig_ec_cleanup;
539 }
540
541 // allocate buffer for signature
542 switch (keydata->crv)
543 {
544 case CJOSE_JWK_EC_P_256:
545 jws->sig_len = 32 * 2;
546 break;
547 case CJOSE_JWK_EC_P_384:
548 jws->sig_len = 48 * 2;
549 break;
550 case CJOSE_JWK_EC_P_521:
551 jws->sig_len = 66 * 2;
552 break;
553 case CJOSE_JWK_EC_INVALID:
554 jws->sig_len = 0;
555 break;
556 }
557
558 jws->sig = (uint8_t *)cjose_get_alloc()(jws->sig_len);
559 if (NULL == jws->sig)
560 {
561 CJOSE_ERROR(err, CJOSE_ERR_NO_MEMORY);
562 goto _cjose_jws_build_sig_ec_cleanup;
563 }
564
565 memset(jws->sig, 0, jws->sig_len);
566
567 const BIGNUM *pr, *ps;
568 #if defined(CJOSE_OPENSSL_11X)
569 ECDSA_SIG_get0(ecdsa_sig, &pr, &ps);
570 #else
571 pr = ecdsa_sig->r;
572 ps = ecdsa_sig->s;
573 #endif
574
575 int rlen = BN_num_bytes(pr);
576 int slen = BN_num_bytes(ps);
577 BN_bn2bin(pr, jws->sig + jws->sig_len / 2 - rlen);
578 BN_bn2bin(ps, jws->sig + jws->sig_len - slen);
579
580 // base64url encode signed digest
581 if (!cjose_base64url_encode((const uint8_t *)jws->sig, jws->sig_len, &jws->sig_b64u, &jws->sig_b64u_len, err))
582 {
583 CJOSE_ERROR(err, CJOSE_ERR_CRYPTO);
584 goto _cjose_jws_build_sig_ec_cleanup;
585 }
586
587 retval = true;
588
589 _cjose_jws_build_sig_ec_cleanup:
590 if (ecdsa_sig)
591 ECDSA_SIG_free(ecdsa_sig);
592
593 return retval;
594 }
595
596 ////////////////////////////////////////////////////////////////////////////////
_cjose_jws_build_cser(cjose_jws_t * jws,cjose_err * err)597 static bool _cjose_jws_build_cser(cjose_jws_t *jws, cjose_err *err)
598 {
599 // both sign and import should be setting these - but check just in case
600 if (NULL == jws->hdr_b64u || NULL == jws->dat_b64u || NULL == jws->sig_b64u)
601 {
602 return false;
603 }
604
605 // compute length of compact serialization
606 jws->cser_len = jws->hdr_b64u_len + jws->dat_b64u_len + jws->sig_b64u_len + 3;
607
608 // allocate buffer for compact serialization
609 assert(NULL == jws->cser);
610 jws->cser = (char *)cjose_get_alloc()(jws->cser_len);
611 if (NULL == jws->cser)
612 {
613 CJOSE_ERROR(err, CJOSE_ERR_NO_MEMORY);
614 return false;
615 }
616
617 // build the compact serialization
618 snprintf(jws->cser, jws->cser_len, "%s.%s.%s", jws->hdr_b64u, jws->dat_b64u, jws->sig_b64u);
619
620 return true;
621 }
622
623 ////////////////////////////////////////////////////////////////////////////////
cjose_jws_sign(const cjose_jwk_t * jwk,cjose_header_t * protected_header,const uint8_t * plaintext,size_t plaintext_len,cjose_err * err)624 cjose_jws_t *cjose_jws_sign(
625 const cjose_jwk_t *jwk, cjose_header_t *protected_header, const uint8_t *plaintext, size_t plaintext_len, cjose_err *err)
626 {
627 cjose_jws_t *jws = NULL;
628
629 if (NULL == jwk || NULL == protected_header || NULL == plaintext)
630 {
631 CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG);
632 return NULL;
633 }
634
635 // allocate and initialize JWS
636 jws = (cjose_jws_t *)cjose_get_alloc()(sizeof(cjose_jws_t));
637 if (NULL == jws)
638 {
639 CJOSE_ERROR(err, CJOSE_ERR_NO_MEMORY);
640 return NULL;
641 }
642 memset(jws, 0, sizeof(cjose_jws_t));
643
644 // build JWS header
645 if (!_cjose_jws_build_hdr(jws, protected_header, err))
646 {
647 cjose_jws_release(jws);
648 return NULL;
649 }
650
651 // validate JWS header
652 if (!_cjose_jws_validate_hdr(jws, err))
653 {
654 cjose_jws_release(jws);
655 return NULL;
656 }
657
658 // build the JWS data segment
659 if (!_cjose_jws_build_dat(jws, plaintext, plaintext_len, err))
660 {
661 cjose_jws_release(jws);
662 return NULL;
663 }
664
665 // build JWS digest (hashed signing input value)
666 if (!jws->fns.digest(jws, jwk, err))
667 {
668 cjose_jws_release(jws);
669 return NULL;
670 }
671
672 // sign the JWS digest
673 if (!jws->fns.sign(jws, jwk, err))
674 {
675 cjose_jws_release(jws);
676 return NULL;
677 }
678
679 // build JWS compact serialization
680 if (!_cjose_jws_build_cser(jws, err))
681 {
682 cjose_jws_release(jws);
683 return NULL;
684 }
685
686 return jws;
687 }
688
689 ////////////////////////////////////////////////////////////////////////////////
cjose_jws_release(cjose_jws_t * jws)690 void cjose_jws_release(cjose_jws_t *jws)
691 {
692 if (NULL == jws)
693 {
694 return;
695 }
696
697 if (NULL != jws->hdr)
698 {
699 json_decref(jws->hdr);
700 }
701
702 cjose_get_dealloc()(jws->hdr_b64u);
703 cjose_get_dealloc()(jws->dat);
704 cjose_get_dealloc()(jws->dat_b64u);
705 cjose_get_dealloc()(jws->dig);
706 cjose_get_dealloc()(jws->sig);
707 cjose_get_dealloc()(jws->sig_b64u);
708 cjose_get_dealloc()(jws->cser);
709 cjose_get_dealloc()(jws);
710 }
711
712 ////////////////////////////////////////////////////////////////////////////////
cjose_jws_export(cjose_jws_t * jws,const char ** compact,cjose_err * err)713 bool cjose_jws_export(cjose_jws_t *jws, const char **compact, cjose_err *err)
714 {
715 if (NULL == jws || NULL == compact)
716 {
717 CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG);
718 return false;
719 }
720
721 if (NULL == jws->cser)
722 {
723 _cjose_jws_build_cser(jws, err);
724 }
725
726 *compact = jws->cser;
727 return true;
728 }
729
730 ////////////////////////////////////////////////////////////////////////////////
_cjose_jws_strcpy(char ** dst,const char * src,int len,cjose_err * err)731 static bool _cjose_jws_strcpy(char **dst, const char *src, int len, cjose_err *err)
732 {
733 *dst = (char *)cjose_get_alloc()(len + 1);
734 if (NULL == dst)
735 {
736 CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG);
737 return false;
738 }
739
740 strncpy(*dst, src, len);
741 (*dst)[len] = 0;
742
743 return true;
744 }
745
746 ////////////////////////////////////////////////////////////////////////////////
cjose_jws_import(const char * cser,size_t cser_len,cjose_err * err)747 cjose_jws_t *cjose_jws_import(const char *cser, size_t cser_len, cjose_err *err)
748 {
749 cjose_jws_t *jws = NULL;
750 size_t len = 0;
751
752 if (NULL == cser)
753 {
754 CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG);
755 return NULL;
756 }
757
758 // allocate and initialize a new JWS object
759 jws = (cjose_jws_t *)cjose_get_alloc()(sizeof(cjose_jws_t));
760 if (NULL == jws)
761 {
762 CJOSE_ERROR(err, CJOSE_ERR_NO_MEMORY);
763 return NULL;
764 }
765 memset(jws, 0, sizeof(cjose_jws_t));
766
767 // find the indexes of the dots
768 int idx = 0;
769 int d[2] = { 0, 0 };
770 for (int i = 0; i < cser_len && idx < 2; ++i)
771 {
772 if (cser[i] == '.')
773 {
774 d[idx++] = i;
775 }
776 }
777
778 // fail if we didn't find both dots
779 if (0 == d[1])
780 {
781 CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG);
782 cjose_jws_release(jws);
783 return NULL;
784 }
785
786 // copy and decode header b64u segment
787 uint8_t *hdr_str = NULL;
788 jws->hdr_b64u_len = d[0];
789 _cjose_jws_strcpy(&jws->hdr_b64u, cser, jws->hdr_b64u_len, err);
790 if (!cjose_base64url_decode(jws->hdr_b64u, jws->hdr_b64u_len, &hdr_str, &len, err) || NULL == hdr_str)
791 {
792 cjose_jws_release(jws);
793 return NULL;
794 }
795
796 // deserialize JSON header
797 jws->hdr = json_loadb((const char *)hdr_str, len, 0, NULL);
798 cjose_get_dealloc()(hdr_str);
799 if (NULL == jws->hdr)
800 {
801 CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG);
802 cjose_jws_release(jws);
803 return NULL;
804 }
805
806 // validate the JSON header segment
807 if (!_cjose_jws_validate_hdr(jws, err))
808 {
809 // make an exception for alg=none so that it will import/parse but not sign/verify
810 json_t *alg_obj = json_object_get(jws->hdr, CJOSE_HDR_ALG);
811 if (NULL == alg_obj)
812 {
813 CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG);
814 return NULL;
815 }
816 const char *alg = json_string_value(alg_obj);
817 if ((!alg) || (strcmp(alg, CJOSE_HDR_ALG_NONE) != 0))
818 {
819 CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG);
820 cjose_jws_release(jws);
821 return NULL;
822 }
823 }
824
825 // copy and b64u decode data segment
826 jws->dat_b64u_len = d[1] - d[0] - 1;
827 _cjose_jws_strcpy(&jws->dat_b64u, cser + d[0] + 1, jws->dat_b64u_len, err);
828 if (!cjose_base64url_decode(jws->dat_b64u, jws->dat_b64u_len, &jws->dat, &jws->dat_len, err))
829 {
830 cjose_jws_release(jws);
831 return NULL;
832 }
833
834 // copy and b64u decode signature segment
835 jws->sig_b64u_len = cser_len - d[1] - 1;
836 _cjose_jws_strcpy(&jws->sig_b64u, cser + d[1] + 1, jws->sig_b64u_len, err);
837 if (!cjose_base64url_decode(jws->sig_b64u, jws->sig_b64u_len, &jws->sig, &jws->sig_len, err))
838 {
839 cjose_jws_release(jws);
840 return NULL;
841 }
842
843 return jws;
844 }
845
846 ////////////////////////////////////////////////////////////////////////////////
_cjose_jws_verify_sig_ps(cjose_jws_t * jws,const cjose_jwk_t * jwk,cjose_err * err)847 static bool _cjose_jws_verify_sig_ps(cjose_jws_t *jws, const cjose_jwk_t *jwk, cjose_err *err)
848 {
849 bool retval = false;
850 uint8_t *em = NULL;
851 size_t em_len = 0;
852
853 // ensure jwk is RSA
854 if (jwk->kty != CJOSE_JWK_KTY_RSA)
855 {
856 CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG);
857 goto _cjose_jws_verify_sig_ps_cleanup;
858 }
859
860 // make sure we have an alg header
861 json_t *alg_obj = json_object_get(jws->hdr, CJOSE_HDR_ALG);
862 if (NULL == alg_obj)
863 {
864 CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG);
865 return false;
866 }
867 const char *alg = json_string_value(alg_obj);
868
869 // build digest using SHA-256/384/512 digest algorithm
870 const EVP_MD *digest_alg = NULL;
871 if (strcmp(alg, CJOSE_HDR_ALG_PS256) == 0)
872 digest_alg = EVP_sha256();
873 else if (strcmp(alg, CJOSE_HDR_ALG_PS384) == 0)
874 digest_alg = EVP_sha384();
875 else if (strcmp(alg, CJOSE_HDR_ALG_PS512) == 0)
876 digest_alg = EVP_sha512();
877
878 if (NULL == digest_alg)
879 {
880 CJOSE_ERROR(err, CJOSE_ERR_CRYPTO);
881 goto _cjose_jws_verify_sig_ps_cleanup;
882 }
883
884 // allocate buffer for encoded message
885 em_len = RSA_size((RSA *)jwk->keydata);
886 em = (uint8_t *)cjose_get_alloc()(em_len);
887 if (NULL == em)
888 {
889 CJOSE_ERROR(err, CJOSE_ERR_CRYPTO);
890 goto _cjose_jws_verify_sig_ps_cleanup;
891 }
892
893 // decrypt signature
894 if (RSA_public_decrypt(jws->sig_len, jws->sig, em, (RSA *)jwk->keydata, RSA_NO_PADDING) != em_len)
895 {
896 CJOSE_ERROR(err, CJOSE_ERR_CRYPTO);
897 goto _cjose_jws_verify_sig_ps_cleanup;
898 }
899
900 // verify decrypted signature data against PSS encoded digest
901 if (RSA_verify_PKCS1_PSS((RSA *)jwk->keydata, jws->dig, digest_alg, em, -1) != 1)
902 {
903 CJOSE_ERROR(err, CJOSE_ERR_CRYPTO);
904 goto _cjose_jws_verify_sig_ps_cleanup;
905 }
906
907 // if we got this far - success
908 retval = true;
909
910 _cjose_jws_verify_sig_ps_cleanup:
911 cjose_get_dealloc()(em);
912
913 return retval;
914 }
915
916 ////////////////////////////////////////////////////////////////////////////////
_cjose_jws_verify_sig_rs(cjose_jws_t * jws,const cjose_jwk_t * jwk,cjose_err * err)917 static bool _cjose_jws_verify_sig_rs(cjose_jws_t *jws, const cjose_jwk_t *jwk, cjose_err *err)
918 {
919 bool retval = false;
920
921 // ensure jwk is RSA
922 if (jwk->kty != CJOSE_JWK_KTY_RSA)
923 {
924 CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG);
925 goto _cjose_jws_verify_sig_rs_cleanup;
926 }
927
928 // make sure we have an alg header
929 json_t *alg_obj = json_object_get(jws->hdr, CJOSE_HDR_ALG);
930 if (NULL == alg_obj)
931 {
932 CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG);
933 return false;
934 }
935 const char *alg = json_string_value(alg_obj);
936
937 // build digest using SHA-256/384/512 digest algorithm
938 int digest_alg = -1;
939 if (strcmp(alg, CJOSE_HDR_ALG_RS256) == 0)
940 digest_alg = NID_sha256;
941 else if (strcmp(alg, CJOSE_HDR_ALG_RS384) == 0)
942 digest_alg = NID_sha384;
943 else if (strcmp(alg, CJOSE_HDR_ALG_RS512) == 0)
944 digest_alg = NID_sha512;
945 if (-1 == digest_alg)
946 {
947 CJOSE_ERROR(err, CJOSE_ERR_CRYPTO);
948 goto _cjose_jws_verify_sig_rs_cleanup;
949 }
950
951 if (RSA_verify(digest_alg, jws->dig, jws->dig_len, jws->sig, jws->sig_len, (RSA *)jwk->keydata) != 1)
952 {
953 CJOSE_ERROR(err, CJOSE_ERR_CRYPTO);
954 goto _cjose_jws_verify_sig_rs_cleanup;
955 }
956
957 // if we got this far - success
958 retval = true;
959
960 _cjose_jws_verify_sig_rs_cleanup:
961
962 return retval;
963 }
964
965 ////////////////////////////////////////////////////////////////////////////////
_cjose_jws_verify_sig_hmac_sha(cjose_jws_t * jws,const cjose_jwk_t * jwk,cjose_err * err)966 static bool _cjose_jws_verify_sig_hmac_sha(cjose_jws_t *jws, const cjose_jwk_t *jwk, cjose_err *err)
967 {
968 bool retval = false;
969
970 // ensure jwk is OCT
971 if (jwk->kty != CJOSE_JWK_KTY_OCT)
972 {
973 CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG);
974 goto _cjose_jws_verify_sig_hmac_sha_cleanup;
975 }
976
977 // verify decrypted digest matches computed digest
978 if ((cjose_const_memcmp(jws->dig, jws->sig, jws->dig_len) != 0) || (jws->sig_len != jws->dig_len))
979 {
980 CJOSE_ERROR(err, CJOSE_ERR_CRYPTO);
981 goto _cjose_jws_verify_sig_hmac_sha_cleanup;
982 }
983
984 // if we got this far - success
985 retval = true;
986
987 _cjose_jws_verify_sig_hmac_sha_cleanup:
988
989 return retval;
990 }
991
992 ////////////////////////////////////////////////////////////////////////////////
_cjose_jws_verify_sig_ec(cjose_jws_t * jws,const cjose_jwk_t * jwk,cjose_err * err)993 static bool _cjose_jws_verify_sig_ec(cjose_jws_t *jws, const cjose_jwk_t *jwk, cjose_err *err)
994 {
995 bool retval = false;
996
997 // ensure jwk is EC
998 if (jwk->kty != CJOSE_JWK_KTY_EC)
999 {
1000 CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG);
1001 return false;
1002 }
1003
1004 ec_keydata *keydata = (ec_keydata *)jwk->keydata;
1005 EC_KEY *ec = keydata->key;
1006
1007 ECDSA_SIG *ecdsa_sig = ECDSA_SIG_new();
1008 int key_len = jws->sig_len / 2;
1009
1010 #if defined(CJOSE_OPENSSL_11X)
1011 BIGNUM *pr = BN_new(), *ps = BN_new();
1012 BN_bin2bn(jws->sig, key_len, pr);
1013 BN_bin2bn(jws->sig + key_len, key_len, ps);
1014 ECDSA_SIG_set0(ecdsa_sig, pr, ps);
1015 #else
1016 BN_bin2bn(jws->sig, key_len, ecdsa_sig->r);
1017 BN_bin2bn(jws->sig + key_len, key_len, ecdsa_sig->s);
1018 #endif
1019
1020 if (ECDSA_do_verify(jws->dig, jws->dig_len, ecdsa_sig, ec) != 1)
1021 {
1022 CJOSE_ERROR(err, CJOSE_ERR_CRYPTO);
1023 goto _cjose_jws_verify_sig_ec_cleanup;
1024 }
1025
1026 // if we got this far - success
1027 retval = true;
1028
1029 _cjose_jws_verify_sig_ec_cleanup:
1030 if (ecdsa_sig)
1031 ECDSA_SIG_free(ecdsa_sig);
1032
1033 return retval;
1034 }
1035
1036 ////////////////////////////////////////////////////////////////////////////////
cjose_jws_verify(cjose_jws_t * jws,const cjose_jwk_t * jwk,cjose_err * err)1037 bool cjose_jws_verify(cjose_jws_t *jws, const cjose_jwk_t *jwk, cjose_err *err)
1038 {
1039 if (NULL == jws || NULL == jwk)
1040 {
1041 CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG);
1042 return false;
1043 }
1044
1045 // validate JWS header
1046 if (!_cjose_jws_validate_hdr(jws, err))
1047 {
1048 return false;
1049 }
1050
1051 // build JWS digest from header and payload (hashed signing input value)
1052 if (!jws->fns.digest(jws, jwk, err))
1053 {
1054 return false;
1055 }
1056
1057 // verify JWS signature
1058 if (!jws->fns.verify(jws, jwk, err))
1059 {
1060 return false;
1061 }
1062
1063 return true;
1064 }
1065
1066 ////////////////////////////////////////////////////////////////////////////////
cjose_jws_get_plaintext(const cjose_jws_t * jws,uint8_t ** plaintext,size_t * plaintext_len,cjose_err * err)1067 bool cjose_jws_get_plaintext(const cjose_jws_t *jws, uint8_t **plaintext, size_t *plaintext_len, cjose_err *err)
1068 {
1069 if (NULL == jws || NULL == plaintext || NULL == jws->dat)
1070 {
1071 CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG);
1072 return false;
1073 }
1074
1075 *plaintext = jws->dat;
1076 *plaintext_len = jws->dat_len;
1077
1078 return true;
1079 }
1080
1081 ////////////////////////////////////////////////////////////////////////////////
cjose_jws_get_protected(cjose_jws_t * jws)1082 cjose_header_t *cjose_jws_get_protected(cjose_jws_t *jws)
1083 {
1084 if (NULL == jws)
1085 {
1086 return NULL;
1087 }
1088
1089 return (cjose_header_t *)jws->hdr;
1090 }
1091