Coverage Report

Created: 2026-09-27 07:07

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/cjose/src/jws.c
Line
Count
Source
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 <openssl/evp.h>
16
#include <openssl/rsa.h>
17
#include <openssl/bn.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
#include "include/util_int.h"
25
26
////////////////////////////////////////////////////////////////////////////////
27
static bool _cjose_jws_build_dig_sha(cjose_jws_t *jws, const cjose_jwk_t *jwk, cjose_err *err);
28
29
static bool _cjose_jws_build_sig_ps(cjose_jws_t *jws, const cjose_jwk_t *jwk, cjose_err *err);
30
31
static bool _cjose_jws_build_dig_hmac_sha(cjose_jws_t *jws, const cjose_jwk_t *jwk, cjose_err *err);
32
33
static bool _cjose_jws_verify_sig_ps(cjose_jws_t *jws, const cjose_jwk_t *jwk, cjose_err *err);
34
35
static bool _cjose_jws_build_sig_rs(cjose_jws_t *jws, const cjose_jwk_t *jwk, cjose_err *err);
36
37
static bool _cjose_jws_verify_sig_rs(cjose_jws_t *jws, const cjose_jwk_t *jwk, cjose_err *err);
38
39
static bool _cjose_jws_build_sig_hmac_sha(cjose_jws_t *jws, const cjose_jwk_t *jwk, cjose_err *err);
40
41
static bool _cjose_jws_verify_sig_hmac_sha(cjose_jws_t *jws, const cjose_jwk_t *jwk, cjose_err *err);
42
43
static bool _cjose_jws_build_sig_ec(cjose_jws_t *jws, const cjose_jwk_t *jwk, cjose_err *err);
44
45
static bool _cjose_jws_verify_sig_ec(cjose_jws_t *jws, const cjose_jwk_t *jwk, cjose_err *err);
46
47
static bool _cjose_jws_validate_ec_key(const char *alg, const cjose_jwk_t *jwk, cjose_err *err);
48
49
#if defined(CJOSE_OPENSSL_111X)
50
static bool _cjose_jws_build_dig_eddsa(cjose_jws_t *jws, const cjose_jwk_t *jwk, cjose_err *err);
51
52
static bool _cjose_jws_build_sig_eddsa(cjose_jws_t *jws, const cjose_jwk_t *jwk, cjose_err *err);
53
54
static bool _cjose_jws_verify_sig_eddsa(cjose_jws_t *jws, const cjose_jwk_t *jwk, cjose_err *err);
55
56
static bool _cjose_jws_validate_okp_key(const char *alg, const cjose_jwk_t *jwk, cjose_err *err);
57
#endif
58
59
static bool _cjose_jws_validate_verify_key(cjose_jws_t *jws, const cjose_jwk_t *jwk, cjose_err *err);
60
61
////////////////////////////////////////////////////////////////////////////////
62
static bool _cjose_jws_build_hdr(cjose_jws_t *jws, cjose_header_t *header, cjose_err *err)
63
0
{
64
    // save header object as part of the JWS (and incr. refcount)
65
0
    jws->hdr = (json_t *)header;
66
0
    json_incref(jws->hdr);
67
68
    // base64url encode the header
69
0
    char *hdr_str = json_dumps(jws->hdr, JSON_ENCODE_ANY | JSON_PRESERVE_ORDER | JSON_COMPACT);
70
0
    if (NULL == hdr_str)
71
0
    {
72
0
        CJOSE_ERROR(err, CJOSE_ERR_NO_MEMORY);
73
0
        return false;
74
0
    }
75
0
    if (!cjose_base64url_encode((const uint8_t *)hdr_str, strlen(hdr_str), &jws->hdr_b64u, &jws->hdr_b64u_len, err))
76
0
    {
77
0
        cjose_get_dealloc()(hdr_str);
78
0
        return false;
79
0
    }
80
0
    cjose_get_dealloc()(hdr_str);
81
82
0
    return true;
83
0
}
84
85
////////////////////////////////////////////////////////////////////////////////
86
static bool _cjose_jws_validate_hdr(cjose_jws_t *jws, cjose_err *err)
87
0
{
88
0
    static const char *const supported_crit_headers[] = { "alg", "cty" };
89
90
0
    if (!_cjose_header_validate_crit((cjose_header_t *)jws->hdr, supported_crit_headers,
91
0
                                     sizeof(supported_crit_headers) / sizeof(supported_crit_headers[0]), err))
92
0
    {
93
0
        return false;
94
0
    }
95
96
    // make sure we have an alg header
97
0
    json_t *alg_obj = json_object_get(jws->hdr, CJOSE_HDR_ALG);
98
0
    if ((NULL == alg_obj) || (!json_is_string(alg_obj)))
99
0
    {
100
0
        CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG);
101
0
        return false;
102
0
    }
103
0
    const char *alg = json_string_value(alg_obj);
104
105
0
    if ((strcmp(alg, CJOSE_HDR_ALG_PS256) == 0) || (strcmp(alg, CJOSE_HDR_ALG_PS384) == 0)
106
0
        || (strcmp(alg, CJOSE_HDR_ALG_PS512) == 0))
107
0
    {
108
0
        jws->fns.digest = _cjose_jws_build_dig_sha;
109
0
        jws->fns.sign = _cjose_jws_build_sig_ps;
110
0
        jws->fns.verify = _cjose_jws_verify_sig_ps;
111
0
    }
112
0
    else if ((strcmp(alg, CJOSE_HDR_ALG_RS256) == 0) || (strcmp(alg, CJOSE_HDR_ALG_RS384) == 0)
113
0
             || (strcmp(alg, CJOSE_HDR_ALG_RS512) == 0))
114
0
    {
115
0
        jws->fns.digest = _cjose_jws_build_dig_sha;
116
0
        jws->fns.sign = _cjose_jws_build_sig_rs;
117
0
        jws->fns.verify = _cjose_jws_verify_sig_rs;
118
0
    }
119
0
    else if ((strcmp(alg, CJOSE_HDR_ALG_HS256) == 0) || (strcmp(alg, CJOSE_HDR_ALG_HS384) == 0)
120
0
             || (strcmp(alg, CJOSE_HDR_ALG_HS512) == 0))
121
0
    {
122
0
        jws->fns.digest = _cjose_jws_build_dig_hmac_sha;
123
0
        jws->fns.sign = _cjose_jws_build_sig_hmac_sha;
124
0
        jws->fns.verify = _cjose_jws_verify_sig_hmac_sha;
125
0
    }
126
0
    else if ((strcmp(alg, CJOSE_HDR_ALG_ES256) == 0) || (strcmp(alg, CJOSE_HDR_ALG_ES256K) == 0)
127
0
             || (strcmp(alg, CJOSE_HDR_ALG_ES384) == 0) || (strcmp(alg, CJOSE_HDR_ALG_ES512) == 0))
128
0
    {
129
0
        jws->fns.digest = _cjose_jws_build_dig_sha;
130
0
        jws->fns.sign = _cjose_jws_build_sig_ec;
131
0
        jws->fns.verify = _cjose_jws_verify_sig_ec;
132
0
    }
133
0
#if defined(CJOSE_OPENSSL_111X)
134
0
    else if ((strcmp(alg, CJOSE_HDR_ALG_ED25519) == 0) || (strcmp(alg, CJOSE_HDR_ALG_ED448) == 0))
135
0
    {
136
0
        jws->fns.digest = _cjose_jws_build_dig_eddsa;
137
0
        jws->fns.sign = _cjose_jws_build_sig_eddsa;
138
0
        jws->fns.verify = _cjose_jws_verify_sig_eddsa;
139
0
    }
140
0
#endif
141
0
    else
142
0
    {
143
0
        CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG);
144
0
        return false;
145
0
    }
146
147
0
    return true;
148
0
}
149
150
////////////////////////////////////////////////////////////////////////////////
151
static bool _cjose_jws_build_dat(cjose_jws_t *jws, const uint8_t *plaintext, size_t plaintext_len, cjose_err *err)
152
0
{
153
    // copy plaintext data
154
0
    jws->dat_len = plaintext_len;
155
0
    jws->dat = (uint8_t *)cjose_get_alloc()(jws->dat_len);
156
0
    if ((NULL == jws->dat) && (jws->dat_len > 0))
157
0
    {
158
0
        CJOSE_ERROR(err, CJOSE_ERR_NO_MEMORY);
159
0
        return false;
160
0
    }
161
0
    memcpy(jws->dat, plaintext, jws->dat_len);
162
163
    // base64url encode data
164
0
    if (!cjose_base64url_encode((const uint8_t *)plaintext, plaintext_len, &jws->dat_b64u, &jws->dat_b64u_len, err))
165
0
    {
166
0
        return false;
167
0
    }
168
169
0
    return true;
170
0
}
171
172
////////////////////////////////////////////////////////////////////////////////
173
static bool _cjose_jws_build_dig_sha(cjose_jws_t *jws, const cjose_jwk_t *jwk, cjose_err *err)
174
0
{
175
0
    bool retval = false;
176
0
    EVP_MD_CTX *ctx = NULL;
177
178
    // make sure we have an alg header
179
0
    json_t *alg_obj = json_object_get(jws->hdr, CJOSE_HDR_ALG);
180
0
    if (NULL == alg_obj)
181
0
    {
182
0
        CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG);
183
0
        return false;
184
0
    }
185
0
    const char *alg = json_string_value(alg_obj);
186
187
    // build digest using SHA-256/384/512 digest algorithm
188
0
    const EVP_MD *digest_alg = NULL;
189
0
    if ((strcmp(alg, CJOSE_HDR_ALG_RS256) == 0) || (strcmp(alg, CJOSE_HDR_ALG_PS256) == 0)
190
0
        || (strcmp(alg, CJOSE_HDR_ALG_ES256) == 0) || (strcmp(alg, CJOSE_HDR_ALG_ES256K) == 0))
191
0
        digest_alg = EVP_sha256();
192
0
    else if ((strcmp(alg, CJOSE_HDR_ALG_RS384) == 0) || (strcmp(alg, CJOSE_HDR_ALG_PS384) == 0)
193
0
             || (strcmp(alg, CJOSE_HDR_ALG_ES384) == 0))
194
0
        digest_alg = EVP_sha384();
195
0
    else if ((strcmp(alg, CJOSE_HDR_ALG_RS512) == 0) || (strcmp(alg, CJOSE_HDR_ALG_PS512) == 0)
196
0
             || (strcmp(alg, CJOSE_HDR_ALG_ES512) == 0))
197
0
        digest_alg = EVP_sha512();
198
199
0
    if (NULL == digest_alg)
200
0
    {
201
0
        CJOSE_ERROR(err, CJOSE_ERR_CRYPTO);
202
0
        goto _cjose_jws_build_dig_sha_cleanup;
203
0
    }
204
205
0
    if (NULL != jws->dig)
206
0
    {
207
0
        _cjose_cleanse_dealloc(jws->dig, jws->dig_len);
208
0
        jws->dig = NULL;
209
0
    }
210
211
    // allocate buffer for digest
212
0
    jws->dig_len = EVP_MD_size(digest_alg);
213
0
    jws->dig = (uint8_t *)cjose_get_alloc()(jws->dig_len);
214
0
    if (NULL == jws->dig)
215
0
    {
216
0
        CJOSE_ERROR(err, CJOSE_ERR_NO_MEMORY);
217
0
        goto _cjose_jws_build_dig_sha_cleanup;
218
0
    }
219
220
    // instantiate and initialize a new mac digest context
221
0
    ctx = EVP_MD_CTX_create();
222
0
    if (NULL == ctx)
223
0
    {
224
0
        CJOSE_ERROR(err, CJOSE_ERR_CRYPTO);
225
0
        goto _cjose_jws_build_dig_sha_cleanup;
226
0
    }
227
0
    EVP_MD_CTX_init(ctx);
228
229
    // create digest as DIGEST(B64U(HEADER).B64U(DATA))
230
0
    if (EVP_DigestInit_ex(ctx, digest_alg, NULL) != 1)
231
0
    {
232
0
        CJOSE_ERROR(err, CJOSE_ERR_CRYPTO);
233
0
        goto _cjose_jws_build_dig_sha_cleanup;
234
0
    }
235
0
    if (EVP_DigestUpdate(ctx, jws->hdr_b64u, jws->hdr_b64u_len) != 1)
236
0
    {
237
0
        CJOSE_ERROR(err, CJOSE_ERR_CRYPTO);
238
0
        goto _cjose_jws_build_dig_sha_cleanup;
239
0
    }
240
0
    if (EVP_DigestUpdate(ctx, ".", 1) != 1)
241
0
    {
242
0
        CJOSE_ERROR(err, CJOSE_ERR_CRYPTO);
243
0
        goto _cjose_jws_build_dig_sha_cleanup;
244
0
    }
245
0
    if (EVP_DigestUpdate(ctx, jws->dat_b64u, jws->dat_b64u_len) != 1)
246
0
    {
247
0
        CJOSE_ERROR(err, CJOSE_ERR_CRYPTO);
248
0
        goto _cjose_jws_build_dig_sha_cleanup;
249
0
    }
250
0
    if (EVP_DigestFinal_ex(ctx, jws->dig, NULL) != 1)
251
0
    {
252
0
        CJOSE_ERROR(err, CJOSE_ERR_CRYPTO);
253
0
        goto _cjose_jws_build_dig_sha_cleanup;
254
0
    }
255
256
    // if we got this far - success
257
0
    retval = true;
258
259
0
_cjose_jws_build_dig_sha_cleanup:
260
0
    if (NULL != ctx)
261
0
    {
262
0
        EVP_MD_CTX_destroy(ctx);
263
0
    }
264
265
0
    return retval;
266
0
}
267
268
////////////////////////////////////////////////////////////////////////////////
269
static bool _cjose_jws_build_dig_hmac_sha(cjose_jws_t *jws, const cjose_jwk_t *jwk, cjose_err *err)
270
0
{
271
0
    bool retval = false;
272
0
    HMAC_CTX *ctx = NULL;
273
274
    // ensure jwk is OCT: only then is keydata the raw key material
275
0
    if (jwk->kty != CJOSE_JWK_KTY_OCT)
276
0
    {
277
0
        CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG);
278
0
        return false;
279
0
    }
280
281
    // make sure we have an alg header
282
0
    json_t *alg_obj = json_object_get(jws->hdr, CJOSE_HDR_ALG);
283
0
    if (NULL == alg_obj)
284
0
    {
285
0
        CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG);
286
0
        return false;
287
0
    }
288
0
    const char *alg = json_string_value(alg_obj);
289
290
    // build digest using SHA-256/384/512 digest algorithm
291
0
    const EVP_MD *digest_alg = NULL;
292
0
    if (strcmp(alg, CJOSE_HDR_ALG_HS256) == 0)
293
0
        digest_alg = EVP_sha256();
294
0
    else if (strcmp(alg, CJOSE_HDR_ALG_HS384) == 0)
295
0
        digest_alg = EVP_sha384();
296
0
    else if (strcmp(alg, CJOSE_HDR_ALG_HS512) == 0)
297
0
        digest_alg = EVP_sha512();
298
299
0
    if (NULL == digest_alg)
300
0
    {
301
0
        CJOSE_ERROR(err, CJOSE_ERR_CRYPTO);
302
0
        goto _cjose_jws_build_dig_hmac_sha_cleanup;
303
0
    }
304
305
    // RFC 7518 section 3.2: an HMAC key MUST be at least as long as the hash output
306
0
    if ((jwk->keysize / 8) < (size_t)EVP_MD_size(digest_alg))
307
0
    {
308
0
        CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG);
309
0
        goto _cjose_jws_build_dig_hmac_sha_cleanup;
310
0
    }
311
312
0
    if (NULL != jws->dig)
313
0
    {
314
0
        _cjose_cleanse_dealloc(jws->dig, jws->dig_len);
315
0
        jws->dig = NULL;
316
0
    }
317
318
    // allocate buffer for digest
319
0
    jws->dig_len = EVP_MD_size(digest_alg);
320
0
    jws->dig = (uint8_t *)cjose_get_alloc()(jws->dig_len);
321
0
    if (NULL == jws->dig)
322
0
    {
323
0
        CJOSE_ERROR(err, CJOSE_ERR_NO_MEMORY);
324
0
        goto _cjose_jws_build_dig_hmac_sha_cleanup;
325
0
    }
326
327
    // instantiate and initialize a new mac digest context
328
0
#if defined(CJOSE_OPENSSL_11X)
329
0
    ctx = HMAC_CTX_new();
330
#else
331
    ctx = cjose_get_alloc()(sizeof(HMAC_CTX));
332
#endif
333
0
    if (NULL == ctx)
334
0
    {
335
0
        CJOSE_ERROR(err, CJOSE_ERR_NO_MEMORY);
336
0
        goto _cjose_jws_build_dig_hmac_sha_cleanup;
337
0
    }
338
339
#if !defined(CJOSE_OPENSSL_11X)
340
    HMAC_CTX_init(ctx);
341
#endif
342
343
    // create digest as DIGEST(B64U(HEADER).B64U(DATA))
344
0
    if (HMAC_Init_ex(ctx, jwk->keydata, jwk->keysize / 8, digest_alg, NULL) != 1)
345
0
    {
346
0
        CJOSE_ERROR(err, CJOSE_ERR_CRYPTO);
347
0
        goto _cjose_jws_build_dig_hmac_sha_cleanup;
348
0
    }
349
0
    if (HMAC_Update(ctx, (const unsigned char *)jws->hdr_b64u, jws->hdr_b64u_len) != 1)
350
0
    {
351
0
        CJOSE_ERROR(err, CJOSE_ERR_CRYPTO);
352
0
        goto _cjose_jws_build_dig_hmac_sha_cleanup;
353
0
    }
354
0
    if (HMAC_Update(ctx, (const unsigned char *)".", 1) != 1)
355
0
    {
356
0
        CJOSE_ERROR(err, CJOSE_ERR_CRYPTO);
357
0
        goto _cjose_jws_build_dig_hmac_sha_cleanup;
358
0
    }
359
0
    if (HMAC_Update(ctx, (const unsigned char *)jws->dat_b64u, jws->dat_b64u_len) != 1)
360
0
    {
361
0
        CJOSE_ERROR(err, CJOSE_ERR_CRYPTO);
362
0
        goto _cjose_jws_build_dig_hmac_sha_cleanup;
363
0
    }
364
0
    if (HMAC_Final(ctx, jws->dig, NULL) != 1)
365
0
    {
366
0
        CJOSE_ERROR(err, CJOSE_ERR_CRYPTO);
367
0
        goto _cjose_jws_build_dig_hmac_sha_cleanup;
368
0
    }
369
370
    // if we got this far - success
371
0
    retval = true;
372
373
0
_cjose_jws_build_dig_hmac_sha_cleanup:
374
0
    if (NULL != ctx)
375
0
    {
376
0
#if defined(CJOSE_OPENSSL_11X)
377
0
        HMAC_CTX_free(ctx);
378
#else
379
        HMAC_CTX_cleanup(ctx);
380
        cjose_get_dealloc()(ctx);
381
#endif
382
0
    }
383
384
0
    return retval;
385
0
}
386
387
////////////////////////////////////////////////////////////////////////////////
388
static bool _cjose_jws_build_sig_ps(cjose_jws_t *jws, const cjose_jwk_t *jwk, cjose_err *err)
389
0
{
390
0
    bool retval = false;
391
0
    uint8_t *em = NULL;
392
0
    size_t em_len = 0;
393
394
    // ensure jwk is private RSA
395
0
    if (jwk->kty != CJOSE_JWK_KTY_RSA)
396
0
    {
397
0
        CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG);
398
0
        goto _cjose_jws_build_sig_ps_cleanup;
399
0
    }
400
0
    RSA *rsa = (RSA *)jwk->keydata;
401
0
    BIGNUM *rsa_n = NULL, *rsa_e = NULL, *rsa_d = NULL;
402
0
    _cjose_jwk_rsa_get(rsa, &rsa_n, &rsa_e, &rsa_d);
403
0
    if (!rsa || !rsa_e || !rsa_n || !rsa_d)
404
0
    {
405
0
        CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG);
406
0
        return false;
407
0
    }
408
409
    // make sure we have an alg header
410
0
    json_t *alg_obj = json_object_get(jws->hdr, CJOSE_HDR_ALG);
411
0
    if (NULL == alg_obj)
412
0
    {
413
0
        CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG);
414
0
        return false;
415
0
    }
416
0
    const char *alg = json_string_value(alg_obj);
417
418
    // build digest using SHA-256/384/512 digest algorithm
419
0
    const EVP_MD *digest_alg = NULL;
420
0
    if (strcmp(alg, CJOSE_HDR_ALG_PS256) == 0)
421
0
        digest_alg = EVP_sha256();
422
0
    else if (strcmp(alg, CJOSE_HDR_ALG_PS384) == 0)
423
0
        digest_alg = EVP_sha384();
424
0
    else if (strcmp(alg, CJOSE_HDR_ALG_PS512) == 0)
425
0
        digest_alg = EVP_sha512();
426
427
0
    if (NULL == digest_alg)
428
0
    {
429
0
        CJOSE_ERROR(err, CJOSE_ERR_CRYPTO);
430
0
        goto _cjose_jws_build_sig_ps_cleanup;
431
0
    }
432
433
    // apply EMSA-PSS encoding (RFC-3447, 8.1.1, step 1)
434
    // (RSA_padding_add_PKCS1_PSS includes PKCS1_MGF1, -1 => saltlen = hashlen)
435
0
    em_len = RSA_size((RSA *)jwk->keydata);
436
0
    em = (uint8_t *)cjose_get_alloc()(em_len);
437
0
    if (NULL == em)
438
0
    {
439
0
        CJOSE_ERROR(err, CJOSE_ERR_NO_MEMORY);
440
0
        goto _cjose_jws_build_sig_ps_cleanup;
441
0
    }
442
0
    if (RSA_padding_add_PKCS1_PSS((RSA *)jwk->keydata, em, jws->dig, digest_alg, -1) != 1)
443
0
    {
444
0
        CJOSE_ERROR(err, CJOSE_ERR_CRYPTO);
445
0
        goto _cjose_jws_build_sig_ps_cleanup;
446
0
    }
447
448
    // sign the digest (RFC-3447, 8.1.1, step 2)
449
0
    jws->sig_len = em_len;
450
0
    jws->sig = (uint8_t *)cjose_get_alloc()(jws->sig_len);
451
0
    if (NULL == jws->sig)
452
0
    {
453
0
        CJOSE_ERROR(err, CJOSE_ERR_NO_MEMORY);
454
0
        goto _cjose_jws_build_sig_ps_cleanup;
455
0
    }
456
457
0
    if (RSA_private_encrypt(em_len, em, jws->sig, (RSA *)jwk->keydata, RSA_NO_PADDING) != jws->sig_len)
458
0
    {
459
0
        CJOSE_ERROR(err, CJOSE_ERR_CRYPTO);
460
0
        goto _cjose_jws_build_sig_ps_cleanup;
461
0
    }
462
463
    // base64url encode signed digest
464
0
    if (!cjose_base64url_encode((const uint8_t *)jws->sig, jws->sig_len, &jws->sig_b64u, &jws->sig_b64u_len, err))
465
0
    {
466
0
        goto _cjose_jws_build_sig_ps_cleanup;
467
0
    }
468
469
    // if we got this far - success
470
0
    retval = true;
471
472
0
_cjose_jws_build_sig_ps_cleanup:
473
0
    cjose_get_dealloc()(em);
474
475
0
    return retval;
476
0
}
477
478
////////////////////////////////////////////////////////////////////////////////
479
static bool _cjose_jws_build_sig_rs(cjose_jws_t *jws, const cjose_jwk_t *jwk, cjose_err *err)
480
0
{
481
    // ensure jwk is private RSA
482
0
    if (jwk->kty != CJOSE_JWK_KTY_RSA)
483
0
    {
484
0
        CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG);
485
0
        return false;
486
0
    }
487
0
    RSA *rsa = (RSA *)jwk->keydata;
488
0
    BIGNUM *rsa_n = NULL, *rsa_e = NULL, *rsa_d = NULL;
489
0
    _cjose_jwk_rsa_get(rsa, &rsa_n, &rsa_e, &rsa_d);
490
0
    if (!rsa || !rsa_e || !rsa_n || !rsa_d)
491
0
    {
492
0
        CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG);
493
0
        return false;
494
0
    }
495
496
    // allocate buffer for signature
497
0
    jws->sig_len = RSA_size((RSA *)jwk->keydata);
498
0
    jws->sig = (uint8_t *)cjose_get_alloc()(jws->sig_len);
499
0
    if (NULL == jws->sig)
500
0
    {
501
0
        CJOSE_ERROR(err, CJOSE_ERR_NO_MEMORY);
502
0
        return false;
503
0
    }
504
505
    // make sure we have an alg header
506
0
    json_t *alg_obj = json_object_get(jws->hdr, CJOSE_HDR_ALG);
507
0
    if (NULL == alg_obj)
508
0
    {
509
0
        CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG);
510
0
        return false;
511
0
    }
512
0
    const char *alg = json_string_value(alg_obj);
513
514
    // build digest using SHA-256/384/512 digest algorithm
515
0
    int digest_alg = -1;
516
0
    if (strcmp(alg, CJOSE_HDR_ALG_RS256) == 0)
517
0
        digest_alg = NID_sha256;
518
0
    else if (strcmp(alg, CJOSE_HDR_ALG_RS384) == 0)
519
0
        digest_alg = NID_sha384;
520
0
    else if (strcmp(alg, CJOSE_HDR_ALG_RS512) == 0)
521
0
        digest_alg = NID_sha512;
522
0
    if (-1 == digest_alg)
523
0
    {
524
0
        CJOSE_ERROR(err, CJOSE_ERR_CRYPTO);
525
0
        return false;
526
0
    }
527
528
0
    unsigned int siglen;
529
0
    if (RSA_sign(digest_alg, jws->dig, jws->dig_len, jws->sig, &siglen, (RSA *)jwk->keydata) != 1)
530
0
    {
531
0
        CJOSE_ERROR(err, CJOSE_ERR_CRYPTO);
532
0
        return false;
533
0
    }
534
0
    jws->sig_len = siglen;
535
536
    // base64url encode signed digest
537
0
    if (!cjose_base64url_encode((const uint8_t *)jws->sig, jws->sig_len, &jws->sig_b64u, &jws->sig_b64u_len, err))
538
0
    {
539
0
        CJOSE_ERROR(err, CJOSE_ERR_CRYPTO);
540
0
        return false;
541
0
    }
542
543
0
    return true;
544
0
}
545
546
static bool _cjose_jws_build_sig_hmac_sha(cjose_jws_t *jws, const cjose_jwk_t *jwk, cjose_err *err)
547
0
{
548
    // ensure jwk is OCT
549
0
    if (jwk->kty != CJOSE_JWK_KTY_OCT)
550
0
    {
551
0
        CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG);
552
0
        return false;
553
0
    }
554
555
    // allocate buffer for signature
556
0
    jws->sig_len = jws->dig_len;
557
0
    jws->sig = (uint8_t *)cjose_get_alloc()(jws->sig_len);
558
0
    if (NULL == jws->sig)
559
0
    {
560
0
        CJOSE_ERROR(err, CJOSE_ERR_NO_MEMORY);
561
0
        return false;
562
0
    }
563
564
0
    memcpy(jws->sig, jws->dig, jws->sig_len);
565
566
    // base64url encode signed digest
567
0
    if (!cjose_base64url_encode((const uint8_t *)jws->sig, jws->sig_len, &jws->sig_b64u, &jws->sig_b64u_len, err))
568
0
    {
569
0
        CJOSE_ERROR(err, CJOSE_ERR_CRYPTO);
570
0
        return false;
571
0
    }
572
573
0
    return true;
574
0
}
575
576
////////////////////////////////////////////////////////////////////////////////
577
static bool _cjose_jws_build_sig_ec(cjose_jws_t *jws, const cjose_jwk_t *jwk, cjose_err *err)
578
0
{
579
0
    bool retval = false;
580
581
0
    const char *alg = json_string_value(json_object_get(jws->hdr, CJOSE_HDR_ALG));
582
0
    if (!_cjose_jws_validate_ec_key(alg, jwk, err))
583
0
    {
584
0
        return false;
585
0
    }
586
587
0
    ec_keydata *keydata = (ec_keydata *)jwk->keydata;
588
0
    EC_KEY *ec = keydata->key;
589
590
0
    ECDSA_SIG *ecdsa_sig = ECDSA_do_sign(jws->dig, jws->dig_len, ec);
591
0
    if (NULL == ecdsa_sig)
592
0
    {
593
0
        CJOSE_ERROR(err, CJOSE_ERR_CRYPTO);
594
0
        goto _cjose_jws_build_sig_ec_cleanup;
595
0
    }
596
597
    // allocate buffer for signature
598
0
    switch (keydata->crv)
599
0
    {
600
0
    case CJOSE_JWK_EC_P_256:
601
0
        jws->sig_len = 32 * 2;
602
0
        break;
603
0
    case CJOSE_JWK_EC_SECP_256K1:
604
0
        jws->sig_len = 32 * 2;
605
0
        break;
606
0
    case CJOSE_JWK_EC_P_384:
607
0
        jws->sig_len = 48 * 2;
608
0
        break;
609
0
    case CJOSE_JWK_EC_P_521:
610
0
        jws->sig_len = 66 * 2;
611
0
        break;
612
0
    case CJOSE_JWK_EC_INVALID:
613
0
        jws->sig_len = 0;
614
0
        break;
615
0
    }
616
617
0
    jws->sig = (uint8_t *)cjose_get_alloc()(jws->sig_len);
618
0
    if (NULL == jws->sig)
619
0
    {
620
0
        CJOSE_ERROR(err, CJOSE_ERR_NO_MEMORY);
621
0
        goto _cjose_jws_build_sig_ec_cleanup;
622
0
    }
623
624
0
    memset(jws->sig, 0, jws->sig_len);
625
626
0
    const BIGNUM *pr, *ps;
627
0
#if defined(CJOSE_OPENSSL_11X)
628
0
    ECDSA_SIG_get0(ecdsa_sig, &pr, &ps);
629
#else
630
    pr = ecdsa_sig->r;
631
    ps = ecdsa_sig->s;
632
#endif
633
634
0
    int rlen = BN_num_bytes(pr);
635
0
    int slen = BN_num_bytes(ps);
636
0
    BN_bn2bin(pr, jws->sig + jws->sig_len / 2 - rlen);
637
0
    BN_bn2bin(ps, jws->sig + jws->sig_len - slen);
638
639
    // base64url encode signed digest
640
0
    if (!cjose_base64url_encode((const uint8_t *)jws->sig, jws->sig_len, &jws->sig_b64u, &jws->sig_b64u_len, err))
641
0
    {
642
0
        CJOSE_ERROR(err, CJOSE_ERR_CRYPTO);
643
0
        goto _cjose_jws_build_sig_ec_cleanup;
644
0
    }
645
646
0
    retval = true;
647
648
0
_cjose_jws_build_sig_ec_cleanup:
649
0
    if (ecdsa_sig)
650
0
        ECDSA_SIG_free(ecdsa_sig);
651
652
0
    return retval;
653
0
}
654
655
////////////////////////////////////////////////////////////////////////////////
656
static bool _cjose_jws_build_cser(cjose_jws_t *jws, cjose_err *err)
657
0
{
658
    // both sign and import should be setting these - but check just in case
659
0
    if (NULL == jws->hdr_b64u || NULL == jws->dat_b64u || NULL == jws->sig_b64u)
660
0
    {
661
0
        CJOSE_ERROR(err, CJOSE_ERR_INVALID_STATE);
662
0
        return false;
663
0
    }
664
665
    // compute length of compact serialization
666
0
    jws->cser_len = jws->hdr_b64u_len + jws->dat_b64u_len + jws->sig_b64u_len + 3;
667
668
0
    if (NULL != jws->cser)
669
0
    {
670
0
        CJOSE_ERROR(err, CJOSE_ERR_INVALID_STATE);
671
0
        return false;
672
0
    }
673
674
    // allocate buffer for compact serialization
675
0
    jws->cser = (char *)cjose_get_alloc()(jws->cser_len);
676
0
    if (NULL == jws->cser)
677
0
    {
678
0
        CJOSE_ERROR(err, CJOSE_ERR_NO_MEMORY);
679
0
        return false;
680
0
    }
681
682
    // build the compact serialization
683
0
    snprintf(jws->cser, jws->cser_len, "%s.%s.%s", jws->hdr_b64u, jws->dat_b64u, jws->sig_b64u);
684
685
0
    return true;
686
0
}
687
688
////////////////////////////////////////////////////////////////////////////////
689
cjose_jws_t *cjose_jws_sign(
690
    const cjose_jwk_t *jwk, cjose_header_t *protected_header, const uint8_t *plaintext, size_t plaintext_len, cjose_err *err)
691
0
{
692
0
    cjose_jws_t *jws = NULL;
693
694
0
    if (NULL == jwk || NULL == protected_header || NULL == plaintext)
695
0
    {
696
0
        CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG);
697
0
        return NULL;
698
0
    }
699
700
    // allocate and initialize JWS
701
0
    jws = (cjose_jws_t *)cjose_get_alloc()(sizeof(cjose_jws_t));
702
0
    if (NULL == jws)
703
0
    {
704
0
        CJOSE_ERROR(err, CJOSE_ERR_NO_MEMORY);
705
0
        return NULL;
706
0
    }
707
0
    memset(jws, 0, sizeof(cjose_jws_t));
708
709
    // build JWS header
710
0
    if (!_cjose_jws_build_hdr(jws, protected_header, err))
711
0
    {
712
0
        cjose_jws_release(jws);
713
0
        return NULL;
714
0
    }
715
716
    // validate JWS header
717
0
    if (!_cjose_jws_validate_hdr(jws, err))
718
0
    {
719
0
        cjose_jws_release(jws);
720
0
        return NULL;
721
0
    }
722
723
    // build the JWS data segment
724
0
    if (!_cjose_jws_build_dat(jws, plaintext, plaintext_len, err))
725
0
    {
726
0
        cjose_jws_release(jws);
727
0
        return NULL;
728
0
    }
729
730
    // build JWS digest (hashed signing input value)
731
0
    if (!jws->fns.digest(jws, jwk, err))
732
0
    {
733
0
        cjose_jws_release(jws);
734
0
        return NULL;
735
0
    }
736
737
    // sign the JWS digest
738
0
    if (!jws->fns.sign(jws, jwk, err))
739
0
    {
740
0
        cjose_jws_release(jws);
741
0
        return NULL;
742
0
    }
743
744
    // build JWS compact serialization
745
0
    if (!_cjose_jws_build_cser(jws, err))
746
0
    {
747
0
        cjose_jws_release(jws);
748
0
        return NULL;
749
0
    }
750
751
0
    return jws;
752
0
}
753
754
////////////////////////////////////////////////////////////////////////////////
755
void cjose_jws_release(cjose_jws_t *jws)
756
0
{
757
0
    if (NULL == jws)
758
0
    {
759
0
        return;
760
0
    }
761
762
0
    if (NULL != jws->hdr)
763
0
    {
764
0
        json_decref(jws->hdr);
765
0
    }
766
767
0
    cjose_get_dealloc()(jws->hdr_b64u);
768
    // the payload may be sensitive: wipe it and the copies that embed it,
769
    // like the decrypted plaintext of a JWE
770
0
    _cjose_cleanse_dealloc(jws->dat, jws->dat_len);
771
0
    _cjose_cleanse_dealloc(jws->dat_b64u, jws->dat_b64u_len);
772
0
    _cjose_cleanse_dealloc(jws->dig, jws->dig_len);
773
0
    _cjose_cleanse_dealloc(jws->sig, jws->sig_len);
774
0
    cjose_get_dealloc()(jws->sig_b64u);
775
0
    _cjose_cleanse_dealloc(jws->cser, jws->cser_len);
776
0
    cjose_get_dealloc()(jws);
777
0
}
778
779
////////////////////////////////////////////////////////////////////////////////
780
bool cjose_jws_export(cjose_jws_t *jws, const char **compact, cjose_err *err)
781
0
{
782
0
    if (NULL == jws || NULL == compact)
783
0
    {
784
0
        CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG);
785
0
        return false;
786
0
    }
787
788
0
    if (NULL == jws->cser)
789
0
    {
790
0
        if (!_cjose_jws_build_cser(jws, err))
791
0
        {
792
0
            return false;
793
0
        }
794
0
    }
795
796
0
    *compact = jws->cser;
797
0
    return true;
798
0
}
799
800
////////////////////////////////////////////////////////////////////////////////
801
static bool _cjose_jws_strcpy(char **dst, const char *src, size_t len, cjose_err *err)
802
0
{
803
0
    *dst = (char *)cjose_get_alloc()(len + 1);
804
0
    if (NULL == *dst)
805
0
    {
806
0
        CJOSE_ERROR(err, CJOSE_ERR_NO_MEMORY);
807
0
        return false;
808
0
    }
809
810
0
    strncpy(*dst, src, len);
811
0
    (*dst)[len] = 0;
812
813
0
    return true;
814
0
}
815
816
////////////////////////////////////////////////////////////////////////////////
817
cjose_jws_t *cjose_jws_import(const char *cser, size_t cser_len, cjose_err *err)
818
0
{
819
0
    cjose_jws_t *jws = NULL;
820
0
    size_t len = 0;
821
822
0
    if (NULL == cser)
823
0
    {
824
0
        CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG);
825
0
        return NULL;
826
0
    }
827
828
    // allocate and initialize a new JWS object
829
0
    jws = (cjose_jws_t *)cjose_get_alloc()(sizeof(cjose_jws_t));
830
0
    if (NULL == jws)
831
0
    {
832
0
        CJOSE_ERROR(err, CJOSE_ERR_NO_MEMORY);
833
0
        return NULL;
834
0
    }
835
0
    memset(jws, 0, sizeof(cjose_jws_t));
836
837
    // find the indexes of the dots; use size_t to match cser_len, an int
838
    // would truncate the offsets for an oversized serialization
839
0
    size_t idx = 0;
840
0
    size_t d[2] = { 0, 0 };
841
0
    for (size_t i = 0; i < cser_len && idx < 2; ++i)
842
0
    {
843
0
        if (cser[i] == '.')
844
0
        {
845
0
            d[idx++] = i;
846
0
        }
847
0
    }
848
849
    // fail if we didn't find both dots
850
0
    if (0 == d[1])
851
0
    {
852
0
        CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG);
853
0
        cjose_jws_release(jws);
854
0
        return NULL;
855
0
    }
856
857
    // copy and decode header b64u segment
858
0
    uint8_t *hdr_str = NULL;
859
0
    jws->hdr_b64u_len = d[0];
860
0
    if (!_cjose_jws_strcpy(&jws->hdr_b64u, cser, jws->hdr_b64u_len, err))
861
0
    {
862
0
        cjose_jws_release(jws);
863
0
        return NULL;
864
0
    }
865
0
    if (!cjose_base64url_decode(jws->hdr_b64u, jws->hdr_b64u_len, &hdr_str, &len, err) || NULL == hdr_str)
866
0
    {
867
0
        cjose_jws_release(jws);
868
0
        return NULL;
869
0
    }
870
871
    // deserialize JSON header
872
0
    jws->hdr = json_loadb((const char *)hdr_str, len, 0, NULL);
873
0
    cjose_get_dealloc()(hdr_str);
874
0
    if (NULL == jws->hdr)
875
0
    {
876
0
        CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG);
877
0
        cjose_jws_release(jws);
878
0
        return NULL;
879
0
    }
880
881
    // validate the JSON header segment
882
0
    if (!_cjose_jws_validate_hdr(jws, err))
883
0
    {
884
        // make an exception for alg=none so that it will import/parse but not sign/verify
885
0
        json_t *alg_obj = json_object_get(jws->hdr, CJOSE_HDR_ALG);
886
0
        if (NULL == alg_obj)
887
0
        {
888
0
            CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG);
889
0
            cjose_jws_release(jws);
890
0
            return NULL;
891
0
        }
892
0
        const char *alg = json_string_value(alg_obj);
893
0
        if ((!alg) || (strcmp(alg, CJOSE_HDR_ALG_NONE) != 0))
894
0
        {
895
0
            CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG);
896
0
            cjose_jws_release(jws);
897
0
            return NULL;
898
0
        }
899
900
        // alg=none is accepted (parse-only): clear the validation error
901
        // recorded above so a successful import does not leave err populated
902
0
        CJOSE_ERROR(err, CJOSE_ERR_NONE);
903
0
    }
904
905
    // copy and b64u decode data segment
906
0
    jws->dat_b64u_len = d[1] - d[0] - 1;
907
0
    if (!_cjose_jws_strcpy(&jws->dat_b64u, cser + d[0] + 1, jws->dat_b64u_len, err))
908
0
    {
909
0
        cjose_jws_release(jws);
910
0
        return NULL;
911
0
    }
912
0
    if (!cjose_base64url_decode(jws->dat_b64u, jws->dat_b64u_len, &jws->dat, &jws->dat_len, err))
913
0
    {
914
0
        cjose_jws_release(jws);
915
0
        return NULL;
916
0
    }
917
918
    // copy and b64u decode signature segment
919
0
    jws->sig_b64u_len = cser_len - d[1] - 1;
920
0
    if (!_cjose_jws_strcpy(&jws->sig_b64u, cser + d[1] + 1, jws->sig_b64u_len, err))
921
0
    {
922
0
        cjose_jws_release(jws);
923
0
        return NULL;
924
0
    }
925
0
    if (!cjose_base64url_decode(jws->sig_b64u, jws->sig_b64u_len, &jws->sig, &jws->sig_len, err))
926
0
    {
927
0
        cjose_jws_release(jws);
928
0
        return NULL;
929
0
    }
930
931
0
    return jws;
932
0
}
933
934
////////////////////////////////////////////////////////////////////////////////
935
static bool _cjose_jws_verify_sig_ps(cjose_jws_t *jws, const cjose_jwk_t *jwk, cjose_err *err)
936
0
{
937
0
    bool retval = false;
938
0
    uint8_t *em = NULL;
939
0
    int em_len = 0;
940
941
    // ensure jwk is RSA
942
0
    if (jwk->kty != CJOSE_JWK_KTY_RSA)
943
0
    {
944
0
        CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG);
945
0
        goto _cjose_jws_verify_sig_ps_cleanup;
946
0
    }
947
948
    // make sure we have an alg header
949
0
    json_t *alg_obj = json_object_get(jws->hdr, CJOSE_HDR_ALG);
950
0
    if (NULL == alg_obj)
951
0
    {
952
0
        CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG);
953
0
        return false;
954
0
    }
955
0
    const char *alg = json_string_value(alg_obj);
956
957
    // build digest using SHA-256/384/512 digest algorithm
958
0
    const EVP_MD *digest_alg = NULL;
959
0
    if (strcmp(alg, CJOSE_HDR_ALG_PS256) == 0)
960
0
        digest_alg = EVP_sha256();
961
0
    else if (strcmp(alg, CJOSE_HDR_ALG_PS384) == 0)
962
0
        digest_alg = EVP_sha384();
963
0
    else if (strcmp(alg, CJOSE_HDR_ALG_PS512) == 0)
964
0
        digest_alg = EVP_sha512();
965
966
0
    if (NULL == digest_alg)
967
0
    {
968
0
        CJOSE_ERROR(err, CJOSE_ERR_CRYPTO);
969
0
        goto _cjose_jws_verify_sig_ps_cleanup;
970
0
    }
971
972
    // allocate buffer for encoded message
973
0
    em_len = RSA_size((RSA *)jwk->keydata);
974
0
    if (em_len <= 0 || jws->sig_len != (size_t)em_len)
975
0
    {
976
0
        CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG);
977
0
        goto _cjose_jws_verify_sig_ps_cleanup;
978
0
    }
979
0
    em = (uint8_t *)cjose_get_alloc()((size_t)em_len);
980
0
    if (NULL == em)
981
0
    {
982
0
        CJOSE_ERROR(err, CJOSE_ERR_NO_MEMORY);
983
0
        goto _cjose_jws_verify_sig_ps_cleanup;
984
0
    }
985
986
    // decrypt signature
987
0
    if (RSA_public_decrypt(em_len, jws->sig, em, (RSA *)jwk->keydata, RSA_NO_PADDING) != em_len)
988
0
    {
989
0
        CJOSE_ERROR(err, CJOSE_ERR_CRYPTO);
990
0
        goto _cjose_jws_verify_sig_ps_cleanup;
991
0
    }
992
993
    // verify decrypted signature data against PSS encoded digest
994
0
    if (RSA_verify_PKCS1_PSS((RSA *)jwk->keydata, jws->dig, digest_alg, em, -1) != 1)
995
0
    {
996
0
        CJOSE_ERROR(err, CJOSE_ERR_CRYPTO);
997
0
        goto _cjose_jws_verify_sig_ps_cleanup;
998
0
    }
999
1000
    // if we got this far - success
1001
0
    retval = true;
1002
1003
0
_cjose_jws_verify_sig_ps_cleanup:
1004
0
    cjose_get_dealloc()(em);
1005
1006
0
    return retval;
1007
0
}
1008
1009
////////////////////////////////////////////////////////////////////////////////
1010
static bool _cjose_jws_verify_sig_rs(cjose_jws_t *jws, const cjose_jwk_t *jwk, cjose_err *err)
1011
0
{
1012
0
    bool retval = false;
1013
1014
    // ensure jwk is RSA
1015
0
    if (jwk->kty != CJOSE_JWK_KTY_RSA)
1016
0
    {
1017
0
        CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG);
1018
0
        goto _cjose_jws_verify_sig_rs_cleanup;
1019
0
    }
1020
1021
    // make sure we have an alg header
1022
0
    json_t *alg_obj = json_object_get(jws->hdr, CJOSE_HDR_ALG);
1023
0
    if (NULL == alg_obj)
1024
0
    {
1025
0
        CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG);
1026
0
        return false;
1027
0
    }
1028
0
    const char *alg = json_string_value(alg_obj);
1029
1030
    // build digest using SHA-256/384/512 digest algorithm
1031
0
    int digest_alg = -1;
1032
0
    if (strcmp(alg, CJOSE_HDR_ALG_RS256) == 0)
1033
0
        digest_alg = NID_sha256;
1034
0
    else if (strcmp(alg, CJOSE_HDR_ALG_RS384) == 0)
1035
0
        digest_alg = NID_sha384;
1036
0
    else if (strcmp(alg, CJOSE_HDR_ALG_RS512) == 0)
1037
0
        digest_alg = NID_sha512;
1038
0
    if (-1 == digest_alg)
1039
0
    {
1040
0
        CJOSE_ERROR(err, CJOSE_ERR_CRYPTO);
1041
0
        goto _cjose_jws_verify_sig_rs_cleanup;
1042
0
    }
1043
1044
0
    if (RSA_verify(digest_alg, jws->dig, jws->dig_len, jws->sig, jws->sig_len, (RSA *)jwk->keydata) != 1)
1045
0
    {
1046
0
        CJOSE_ERROR(err, CJOSE_ERR_CRYPTO);
1047
0
        goto _cjose_jws_verify_sig_rs_cleanup;
1048
0
    }
1049
1050
    // if we got this far - success
1051
0
    retval = true;
1052
1053
0
_cjose_jws_verify_sig_rs_cleanup:
1054
1055
0
    return retval;
1056
0
}
1057
1058
////////////////////////////////////////////////////////////////////////////////
1059
static bool _cjose_jws_verify_sig_hmac_sha(cjose_jws_t *jws, const cjose_jwk_t *jwk, cjose_err *err)
1060
0
{
1061
0
    bool retval = false;
1062
0
    int diff = 0;
1063
1064
    // ensure jwk is OCT
1065
0
    if (jwk->kty != CJOSE_JWK_KTY_OCT)
1066
0
    {
1067
0
        CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG);
1068
0
        goto _cjose_jws_verify_sig_hmac_sha_cleanup;
1069
0
    }
1070
1071
    // verify decrypted digest matches computed digest
1072
0
    diff |= (jws->sig_len != jws->dig_len);
1073
0
    if (jws->sig_len == jws->dig_len)
1074
0
    {
1075
0
        diff |= cjose_const_memcmp(jws->dig, jws->sig, jws->dig_len);
1076
0
    }
1077
0
    if (diff != 0)
1078
0
    {
1079
0
        CJOSE_ERROR(err, CJOSE_ERR_CRYPTO);
1080
0
        goto _cjose_jws_verify_sig_hmac_sha_cleanup;
1081
0
    }
1082
1083
    // if we got this far - success
1084
0
    retval = true;
1085
1086
0
_cjose_jws_verify_sig_hmac_sha_cleanup:
1087
1088
0
    return retval;
1089
0
}
1090
1091
////////////////////////////////////////////////////////////////////////////////
1092
static bool _cjose_jws_verify_sig_ec(cjose_jws_t *jws, const cjose_jwk_t *jwk, cjose_err *err)
1093
0
{
1094
0
    bool retval = false;
1095
1096
    // ensure jwk is EC
1097
0
    if (jwk->kty != CJOSE_JWK_KTY_EC)
1098
0
    {
1099
0
        CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG);
1100
0
        return false;
1101
0
    }
1102
1103
0
    ec_keydata *keydata = (ec_keydata *)jwk->keydata;
1104
0
    EC_KEY *ec = keydata->key;
1105
1106
    // the JWS ECDSA signature is the fixed-length concatenation R || S, each
1107
    // the curve's coordinate size (RFC 7518 section 3.4); reject any other
1108
    // length before splitting it so a non-canonical signature (e.g. a trailing
1109
    // byte dropped by the sig_len/2 split) cannot verify
1110
0
    size_t coordlen = 0;
1111
0
    switch (keydata->crv)
1112
0
    {
1113
0
    case CJOSE_JWK_EC_P_256:
1114
0
        coordlen = 32;
1115
0
        break;
1116
0
    case CJOSE_JWK_EC_SECP_256K1:
1117
0
        coordlen = 32;
1118
0
        break;
1119
0
    case CJOSE_JWK_EC_P_384:
1120
0
        coordlen = 48;
1121
0
        break;
1122
0
    case CJOSE_JWK_EC_P_521:
1123
0
        coordlen = 66;
1124
0
        break;
1125
0
    case CJOSE_JWK_EC_INVALID:
1126
0
        coordlen = 0;
1127
0
        break;
1128
0
    }
1129
0
    if (0 == coordlen || jws->sig_len != coordlen * 2)
1130
0
    {
1131
0
        CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG);
1132
0
        return false;
1133
0
    }
1134
1135
0
    ECDSA_SIG *ecdsa_sig = ECDSA_SIG_new();
1136
0
    if (ecdsa_sig == NULL)
1137
0
    {
1138
0
        CJOSE_ERROR(err, CJOSE_ERR_CRYPTO);
1139
0
        goto _cjose_jws_verify_sig_ec_cleanup;
1140
0
    }
1141
0
    int key_len = jws->sig_len / 2;
1142
1143
0
#if defined(CJOSE_OPENSSL_11X)
1144
0
    BIGNUM *pr = BN_new();
1145
0
    BIGNUM *ps = BN_new();
1146
0
    if (pr == NULL || ps == NULL)
1147
0
    {
1148
0
        BN_free(pr);
1149
0
        BN_free(ps);
1150
0
        CJOSE_ERROR(err, CJOSE_ERR_CRYPTO);
1151
0
        goto _cjose_jws_verify_sig_ec_cleanup;
1152
0
    }
1153
0
    BN_bin2bn(jws->sig, key_len, pr);
1154
0
    BN_bin2bn(jws->sig + key_len, key_len, ps);
1155
0
    ECDSA_SIG_set0(ecdsa_sig, pr, ps); // takes ownership of pr and ps
1156
#else
1157
    BN_bin2bn(jws->sig, key_len, ecdsa_sig->r);
1158
    BN_bin2bn(jws->sig + key_len, key_len, ecdsa_sig->s);
1159
#endif
1160
1161
0
    if (ECDSA_do_verify(jws->dig, jws->dig_len, ecdsa_sig, ec) != 1)
1162
0
    {
1163
0
        CJOSE_ERROR(err, CJOSE_ERR_CRYPTO);
1164
0
        goto _cjose_jws_verify_sig_ec_cleanup;
1165
0
    }
1166
1167
    // if we got this far - success
1168
0
    retval = true;
1169
1170
0
_cjose_jws_verify_sig_ec_cleanup:
1171
0
    if (ecdsa_sig)
1172
0
        ECDSA_SIG_free(ecdsa_sig);
1173
1174
0
    return retval;
1175
0
}
1176
1177
////////////////////////////////////////////////////////////////////////////////
1178
static bool _cjose_jws_validate_ec_key(const char *alg, const cjose_jwk_t *jwk, cjose_err *err)
1179
0
{
1180
0
    if (jwk->kty != CJOSE_JWK_KTY_EC)
1181
0
    {
1182
0
        CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG);
1183
0
        return false;
1184
0
    }
1185
1186
0
    ec_keydata *keydata = (ec_keydata *)jwk->keydata;
1187
1188
    // RFC 8812 requires secp256k1 keys to be used only with ES256K and
1189
    // requires ES256K to use a secp256k1 key.
1190
0
    if ((strcmp(alg, CJOSE_HDR_ALG_ES256K) == 0) != (keydata->crv == CJOSE_JWK_EC_SECP_256K1))
1191
0
    {
1192
0
        CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG);
1193
0
        return false;
1194
0
    }
1195
1196
0
    return true;
1197
0
}
1198
1199
////////////////////////////////////////////////////////////////////////////////
1200
#if defined(CJOSE_OPENSSL_111X)
1201
1202
// the fixed size of an EdDSA signature (RFC 8032 sections 5.1.6 and 5.2.6)
1203
static size_t _cjose_jws_eddsa_sig_len(cjose_jwk_okp_curve crv)
1204
0
{
1205
0
    switch (crv)
1206
0
    {
1207
0
    case CJOSE_JWK_OKP_ED25519:
1208
0
        return 64;
1209
0
    case CJOSE_JWK_OKP_ED448:
1210
0
        return 114;
1211
0
    default:
1212
0
        return 0;
1213
0
    }
1214
0
}
1215
1216
////////////////////////////////////////////////////////////////////////////////
1217
static bool _cjose_jws_validate_okp_key(const char *alg, const cjose_jwk_t *jwk, cjose_err *err)
1218
0
{
1219
0
    if (jwk->kty != CJOSE_JWK_KTY_OKP)
1220
0
    {
1221
0
        CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG);
1222
0
        return false;
1223
0
    }
1224
1225
0
    okp_keydata *keydata = (okp_keydata *)jwk->keydata;
1226
1227
    // RFC 9864 binds the fully-specified Ed25519 and Ed448 identifiers to a key
1228
    // of that curve, and an X25519/X448 key agreement key never signs (RFC 8037
1229
    // section 3.1); the polymorphic "EdDSA" identifier of RFC 8037, which RFC
1230
    // 9864 deprecates, is not supported
1231
0
    bool valid = false;
1232
0
    if (strcmp(alg, CJOSE_HDR_ALG_ED25519) == 0)
1233
0
    {
1234
0
        valid = (keydata->crv == CJOSE_JWK_OKP_ED25519);
1235
0
    }
1236
0
    else if (strcmp(alg, CJOSE_HDR_ALG_ED448) == 0)
1237
0
    {
1238
0
        valid = (keydata->crv == CJOSE_JWK_OKP_ED448);
1239
0
    }
1240
1241
0
    if (!valid)
1242
0
    {
1243
0
        CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG);
1244
0
        return false;
1245
0
    }
1246
1247
0
    return true;
1248
0
}
1249
1250
////////////////////////////////////////////////////////////////////////////////
1251
static bool _cjose_jws_build_dig_eddsa(cjose_jws_t *jws, const cjose_jwk_t *jwk, cjose_err *err)
1252
0
{
1253
    // PureEdDSA (RFC 8037 section 3.1) signs the message itself without a
1254
    // pre-hash, and OpenSSL only offers the one-shot EVP_DigestSign and
1255
    // EVP_DigestVerify for it: the "digest" is the JWS signing input
1256
    // B64U(HEADER).B64U(DATA) (RFC 7515 section 5.1)
1257
0
    if (NULL != jws->dig)
1258
0
    {
1259
0
        _cjose_cleanse_dealloc(jws->dig, jws->dig_len);
1260
0
        jws->dig = NULL;
1261
0
    }
1262
1263
    // guard the length of the signing input (+ '.' separator) against size_t overflow
1264
0
    if (jws->dat_b64u_len > SIZE_MAX - 1 || jws->hdr_b64u_len > SIZE_MAX - 1 - jws->dat_b64u_len)
1265
0
    {
1266
0
        CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG);
1267
0
        return false;
1268
0
    }
1269
1270
0
    jws->dig_len = jws->hdr_b64u_len + 1 + jws->dat_b64u_len;
1271
0
    jws->dig = (uint8_t *)cjose_get_alloc()(jws->dig_len);
1272
0
    if (NULL == jws->dig)
1273
0
    {
1274
0
        CJOSE_ERROR(err, CJOSE_ERR_NO_MEMORY);
1275
0
        return false;
1276
0
    }
1277
0
    memcpy(jws->dig, jws->hdr_b64u, jws->hdr_b64u_len);
1278
0
    jws->dig[jws->hdr_b64u_len] = '.';
1279
0
    memcpy(jws->dig + jws->hdr_b64u_len + 1, jws->dat_b64u, jws->dat_b64u_len);
1280
1281
0
    return true;
1282
0
}
1283
1284
////////////////////////////////////////////////////////////////////////////////
1285
static bool _cjose_jws_build_sig_eddsa(cjose_jws_t *jws, const cjose_jwk_t *jwk, cjose_err *err)
1286
0
{
1287
0
    bool retval = false;
1288
0
    EVP_MD_CTX *ctx = NULL;
1289
0
    size_t sig_len = 0;
1290
1291
0
    const char *alg = json_string_value(json_object_get(jws->hdr, CJOSE_HDR_ALG));
1292
0
    if (!_cjose_jws_validate_okp_key(alg, jwk, err))
1293
0
    {
1294
0
        return false;
1295
0
    }
1296
1297
0
    okp_keydata *keydata = (okp_keydata *)jwk->keydata;
1298
1299
    // signing needs the private key: OpenSSL 1.1.1 and 3.0.0 to 3.0.7 sign
1300
    // with the missing private key of a public-only key instead of failing
1301
    // (3.0.8 added the guard), so refuse it here rather than rely on OpenSSL;
1302
    // 1.1.1 only reports the absence of the private key when asked to copy
1303
    // it out, so copy it into a scratch buffer that is wiped right after
1304
0
    size_t priv_len = 0;
1305
0
    if (1 != EVP_PKEY_get_raw_private_key(keydata->key, NULL, &priv_len) || 0 == priv_len)
1306
0
    {
1307
0
        CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG);
1308
0
        return false;
1309
0
    }
1310
0
    uint8_t *priv = (uint8_t *)cjose_get_alloc()(priv_len);
1311
0
    if (NULL == priv)
1312
0
    {
1313
0
        CJOSE_ERROR(err, CJOSE_ERR_NO_MEMORY);
1314
0
        return false;
1315
0
    }
1316
0
    int has_priv = EVP_PKEY_get_raw_private_key(keydata->key, priv, &priv_len);
1317
0
    _cjose_cleanse_dealloc(priv, priv_len);
1318
0
    if (1 != has_priv)
1319
0
    {
1320
0
        CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG);
1321
0
        return false;
1322
0
    }
1323
1324
0
    ctx = EVP_MD_CTX_new();
1325
0
    if (NULL == ctx)
1326
0
    {
1327
0
        CJOSE_ERROR(err, CJOSE_ERR_CRYPTO);
1328
0
        goto _cjose_jws_build_sig_eddsa_cleanup;
1329
0
    }
1330
1331
    // PureEdDSA takes no digest algorithm
1332
0
    if (1 != EVP_DigestSignInit(ctx, NULL, NULL, NULL, keydata->key))
1333
0
    {
1334
0
        CJOSE_ERROR(err, CJOSE_ERR_CRYPTO);
1335
0
        goto _cjose_jws_build_sig_eddsa_cleanup;
1336
0
    }
1337
1338
    // allocate buffer for signature: the fixed size of the curve
1339
0
    jws->sig_len = _cjose_jws_eddsa_sig_len(keydata->crv);
1340
0
    jws->sig = (uint8_t *)cjose_get_alloc()(jws->sig_len);
1341
0
    if (NULL == jws->sig)
1342
0
    {
1343
0
        CJOSE_ERROR(err, CJOSE_ERR_NO_MEMORY);
1344
0
        goto _cjose_jws_build_sig_eddsa_cleanup;
1345
0
    }
1346
1347
    // sign the signing input in one shot
1348
0
    sig_len = jws->sig_len;
1349
0
    if (1 != EVP_DigestSign(ctx, jws->sig, &sig_len, jws->dig, jws->dig_len) || sig_len != jws->sig_len)
1350
0
    {
1351
0
        CJOSE_ERROR(err, CJOSE_ERR_CRYPTO);
1352
0
        goto _cjose_jws_build_sig_eddsa_cleanup;
1353
0
    }
1354
1355
    // base64url encode the signature
1356
0
    if (!cjose_base64url_encode((const uint8_t *)jws->sig, jws->sig_len, &jws->sig_b64u, &jws->sig_b64u_len, err))
1357
0
    {
1358
0
        goto _cjose_jws_build_sig_eddsa_cleanup;
1359
0
    }
1360
1361
    // if we got this far - success
1362
0
    retval = true;
1363
1364
0
_cjose_jws_build_sig_eddsa_cleanup:
1365
0
    EVP_MD_CTX_free(ctx);
1366
1367
0
    return retval;
1368
0
}
1369
1370
////////////////////////////////////////////////////////////////////////////////
1371
static bool _cjose_jws_verify_sig_eddsa(cjose_jws_t *jws, const cjose_jwk_t *jwk, cjose_err *err)
1372
0
{
1373
0
    bool retval = false;
1374
0
    EVP_MD_CTX *ctx = NULL;
1375
1376
0
    const char *alg = json_string_value(json_object_get(jws->hdr, CJOSE_HDR_ALG));
1377
0
    if (!_cjose_jws_validate_okp_key(alg, jwk, err))
1378
0
    {
1379
0
        return false;
1380
0
    }
1381
1382
0
    okp_keydata *keydata = (okp_keydata *)jwk->keydata;
1383
1384
    // the signature has the fixed size of the curve; reject any other length
1385
    // before handing it to OpenSSL
1386
0
    if (jws->sig_len != _cjose_jws_eddsa_sig_len(keydata->crv))
1387
0
    {
1388
0
        CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG);
1389
0
        return false;
1390
0
    }
1391
1392
0
    ctx = EVP_MD_CTX_new();
1393
0
    if (NULL == ctx)
1394
0
    {
1395
0
        CJOSE_ERROR(err, CJOSE_ERR_CRYPTO);
1396
0
        goto _cjose_jws_verify_sig_eddsa_cleanup;
1397
0
    }
1398
1399
    // PureEdDSA takes no digest algorithm
1400
0
    if (1 != EVP_DigestVerifyInit(ctx, NULL, NULL, NULL, keydata->key))
1401
0
    {
1402
0
        CJOSE_ERROR(err, CJOSE_ERR_CRYPTO);
1403
0
        goto _cjose_jws_verify_sig_eddsa_cleanup;
1404
0
    }
1405
1406
    // verify the signature over the signing input in one shot
1407
0
    if (1 != EVP_DigestVerify(ctx, jws->sig, jws->sig_len, jws->dig, jws->dig_len))
1408
0
    {
1409
0
        CJOSE_ERROR(err, CJOSE_ERR_CRYPTO);
1410
0
        goto _cjose_jws_verify_sig_eddsa_cleanup;
1411
0
    }
1412
1413
    // if we got this far - success
1414
0
    retval = true;
1415
1416
0
_cjose_jws_verify_sig_eddsa_cleanup:
1417
0
    EVP_MD_CTX_free(ctx);
1418
1419
0
    return retval;
1420
0
}
1421
1422
#endif // CJOSE_OPENSSL_111X
1423
1424
////////////////////////////////////////////////////////////////////////////////
1425
static bool _cjose_jws_validate_verify_key(cjose_jws_t *jws, const cjose_jwk_t *jwk, cjose_err *err)
1426
0
{
1427
0
    json_t *alg_obj = json_object_get(jws->hdr, CJOSE_HDR_ALG);
1428
0
    if (NULL == alg_obj || !json_is_string(alg_obj))
1429
0
    {
1430
0
        CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG);
1431
0
        return false;
1432
0
    }
1433
1434
0
    const char *alg = json_string_value(alg_obj);
1435
0
    if (0 == strcmp(alg, CJOSE_HDR_ALG_NONE))
1436
0
    {
1437
0
        CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG);
1438
0
        return false;
1439
0
    }
1440
1441
0
    if (((0 == strcmp(alg, CJOSE_HDR_ALG_PS256)) || (0 == strcmp(alg, CJOSE_HDR_ALG_PS384))
1442
0
         || (0 == strcmp(alg, CJOSE_HDR_ALG_PS512)) || (0 == strcmp(alg, CJOSE_HDR_ALG_RS256))
1443
0
         || (0 == strcmp(alg, CJOSE_HDR_ALG_RS384)) || (0 == strcmp(alg, CJOSE_HDR_ALG_RS512)))
1444
0
        && jwk->kty != CJOSE_JWK_KTY_RSA)
1445
0
    {
1446
0
        CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG);
1447
0
        return false;
1448
0
    }
1449
1450
0
    if (((0 == strcmp(alg, CJOSE_HDR_ALG_HS256)) || (0 == strcmp(alg, CJOSE_HDR_ALG_HS384))
1451
0
         || (0 == strcmp(alg, CJOSE_HDR_ALG_HS512)))
1452
0
        && jwk->kty != CJOSE_JWK_KTY_OCT)
1453
0
    {
1454
0
        CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG);
1455
0
        return false;
1456
0
    }
1457
1458
0
    if ((0 == strcmp(alg, CJOSE_HDR_ALG_ES256)) || (0 == strcmp(alg, CJOSE_HDR_ALG_ES256K))
1459
0
        || (0 == strcmp(alg, CJOSE_HDR_ALG_ES384)) || (0 == strcmp(alg, CJOSE_HDR_ALG_ES512)))
1460
0
    {
1461
0
        if (!_cjose_jws_validate_ec_key(alg, jwk, err))
1462
0
        {
1463
0
            return false;
1464
0
        }
1465
0
    }
1466
1467
0
#if defined(CJOSE_OPENSSL_111X)
1468
0
    if ((0 == strcmp(alg, CJOSE_HDR_ALG_ED25519)) || (0 == strcmp(alg, CJOSE_HDR_ALG_ED448)))
1469
0
    {
1470
0
        if (!_cjose_jws_validate_okp_key(alg, jwk, err))
1471
0
        {
1472
0
            return false;
1473
0
        }
1474
0
    }
1475
0
#endif
1476
1477
0
    return true;
1478
0
}
1479
1480
////////////////////////////////////////////////////////////////////////////////
1481
bool cjose_jws_verify(cjose_jws_t *jws, const cjose_jwk_t *jwk, cjose_err *err)
1482
0
{
1483
0
    if (NULL == jws || NULL == jwk)
1484
0
    {
1485
0
        CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG);
1486
0
        return false;
1487
0
    }
1488
1489
    // validate JWS header
1490
0
    if (!_cjose_jws_validate_hdr(jws, err))
1491
0
    {
1492
0
        return false;
1493
0
    }
1494
1495
0
    if (!_cjose_jws_validate_verify_key(jws, jwk, err))
1496
0
    {
1497
0
        return false;
1498
0
    }
1499
1500
    // build JWS digest from header and payload (hashed signing input value)
1501
0
    if (!jws->fns.digest(jws, jwk, err))
1502
0
    {
1503
0
        return false;
1504
0
    }
1505
1506
    // verify JWS signature
1507
0
    if (!jws->fns.verify(jws, jwk, err))
1508
0
    {
1509
0
        return false;
1510
0
    }
1511
1512
0
    return true;
1513
0
}
1514
1515
////////////////////////////////////////////////////////////////////////////////
1516
bool cjose_jws_get_plaintext(const cjose_jws_t *jws, uint8_t **plaintext, size_t *plaintext_len, cjose_err *err)
1517
0
{
1518
0
    if (NULL == jws || NULL == plaintext || NULL == jws->dat)
1519
0
    {
1520
0
        CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG);
1521
0
        return false;
1522
0
    }
1523
1524
0
    *plaintext = jws->dat;
1525
0
    *plaintext_len = jws->dat_len;
1526
1527
0
    return true;
1528
0
}
1529
1530
////////////////////////////////////////////////////////////////////////////////
1531
cjose_header_t *cjose_jws_get_protected(cjose_jws_t *jws)
1532
0
{
1533
0
    if (NULL == jws)
1534
0
    {
1535
0
        return NULL;
1536
0
    }
1537
1538
0
    return (cjose_header_t *)jws->hdr;
1539
0
}