Coverage Report

Created: 2026-08-31 07:13

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