Coverage Report

Created: 2026-08-31 07:13

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/cjose/src/header.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 <stdlib.h>
9
#include <string.h>
10
#include <jansson.h>
11
#include "cjose/header.h"
12
#include "include/header_int.h"
13
14
const char *CJOSE_HDR_ALG = "alg";
15
const char *CJOSE_HDR_ALG_NONE = "none";
16
const char *CJOSE_HDR_ALG_ECDH_ES = "ECDH-ES";
17
const char *CJOSE_HDR_ALG_RSA_OAEP = "RSA-OAEP";
18
const char *CJOSE_HDR_ALG_RSA1_5 = "RSA1_5";
19
const char *CJOSE_HDR_ALG_A128KW = "A128KW";
20
const char *CJOSE_HDR_ALG_A192KW = "A192KW";
21
const char *CJOSE_HDR_ALG_A256KW = "A256KW";
22
const char *CJOSE_HDR_ALG_DIR = "dir";
23
const char *CJOSE_HDR_ALG_PS256 = "PS256";
24
const char *CJOSE_HDR_ALG_PS384 = "PS384";
25
const char *CJOSE_HDR_ALG_PS512 = "PS512";
26
const char *CJOSE_HDR_ALG_RS256 = "RS256";
27
const char *CJOSE_HDR_ALG_RS384 = "RS384";
28
const char *CJOSE_HDR_ALG_RS512 = "RS512";
29
const char *CJOSE_HDR_ALG_HS256 = "HS256";
30
const char *CJOSE_HDR_ALG_HS384 = "HS384";
31
const char *CJOSE_HDR_ALG_HS512 = "HS512";
32
const char *CJOSE_HDR_ALG_ES256 = "ES256";
33
const char *CJOSE_HDR_ALG_ES384 = "ES384";
34
const char *CJOSE_HDR_ALG_ES512 = "ES512";
35
36
const char *CJOSE_HDR_ENC = "enc";
37
const char *CJOSE_HDR_ENC_A128GCM = "A128GCM";
38
const char *CJOSE_HDR_ENC_A192GCM = "A192GCM";
39
const char *CJOSE_HDR_ENC_A256GCM = "A256GCM";
40
const char *CJOSE_HDR_ENC_A128CBC_HS256 = "A128CBC-HS256";
41
const char *CJOSE_HDR_ENC_A192CBC_HS384 = "A192CBC-HS384";
42
const char *CJOSE_HDR_ENC_A256CBC_HS512 = "A256CBC-HS512";
43
44
const char *CJOSE_HDR_CTY = "cty";
45
46
const char *CJOSE_HDR_KID = "kid";
47
48
const char *CJOSE_HDR_EPK = "epk";
49
50
const char *CJOSE_HDR_APU = "apu";
51
const char *CJOSE_HDR_APV = "apv";
52
53
static const char *CJOSE_HDR_CRIT = "crit";
54
55
////////////////////////////////////////////////////////////////////////////////
56
bool _cjose_header_validate_crit(cjose_header_t *header, const char *const *supported, size_t supported_len, cjose_err *err)
57
18.6k
{
58
18.6k
    if (NULL == header)
59
12.4k
    {
60
12.4k
        return true;
61
12.4k
    }
62
63
6.21k
    json_t *crit = json_object_get((json_t *)header, CJOSE_HDR_CRIT);
64
6.21k
    if (NULL == crit)
65
6.21k
    {
66
6.21k
        return true;
67
6.21k
    }
68
69
0
    if (!json_is_array(crit))
70
0
    {
71
0
        CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG);
72
0
        return false;
73
0
    }
74
75
    // RFC 7515 section 4.1.11: if present, the "crit" list MUST NOT be empty
76
0
    if (0 == json_array_size(crit))
77
0
    {
78
0
        CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG);
79
0
        return false;
80
0
    }
81
82
0
    size_t index = 0;
83
0
    json_t *entry = NULL;
84
0
    json_array_foreach(crit, index, entry)
85
0
    {
86
0
        if (!json_is_string(entry))
87
0
        {
88
0
            CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG);
89
0
            return false;
90
0
        }
91
92
0
        const char *name = json_string_value(entry);
93
0
        bool found = false;
94
0
        for (size_t i = 0; i < supported_len; i++)
95
0
        {
96
0
            if (0 == strcmp(name, supported[i]))
97
0
            {
98
0
                found = true;
99
0
                break;
100
0
            }
101
0
        }
102
103
0
        if (!found)
104
0
        {
105
0
            CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG);
106
0
            return false;
107
0
        }
108
0
    }
109
110
0
    return true;
111
0
}
112
113
////////////////////////////////////////////////////////////////////////////////
114
cjose_header_t *cjose_header_new(cjose_err *err)
115
0
{
116
0
    cjose_header_t *retval = (cjose_header_t *)json_object();
117
0
    if (NULL == retval)
118
0
    {
119
0
        CJOSE_ERROR(err, CJOSE_ERR_NO_MEMORY);
120
0
    }
121
0
    return retval;
122
0
}
123
124
////////////////////////////////////////////////////////////////////////////////
125
cjose_header_t *cjose_header_retain(cjose_header_t *header)
126
0
{
127
0
    if (NULL != header)
128
0
    {
129
0
        header = (cjose_header_t *)json_incref((json_t *)header);
130
0
    }
131
0
    return header;
132
0
}
133
134
////////////////////////////////////////////////////////////////////////////////
135
void cjose_header_release(cjose_header_t *header)
136
0
{
137
0
    if (NULL != header)
138
0
    {
139
0
        json_decref((json_t *)header);
140
0
    }
141
0
}
142
143
////////////////////////////////////////////////////////////////////////////////
144
bool cjose_header_set(cjose_header_t *header, const char *attr, const char *value, cjose_err *err)
145
0
{
146
0
    if (NULL == header || NULL == attr || NULL == value)
147
0
    {
148
0
        CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG);
149
0
        return false;
150
0
    }
151
152
0
    json_t *value_obj = json_string(value);
153
0
    if (NULL == value_obj)
154
0
    {
155
0
        CJOSE_ERROR(err, CJOSE_ERR_NO_MEMORY);
156
0
        return false;
157
0
    }
158
159
    // json_object_set_new fails on OOM or an invalid attr key, and releases
160
    // value_obj either way; don't report success with the attribute unset
161
0
    if (0 != json_object_set_new((json_t *)header, attr, value_obj))
162
0
    {
163
0
        CJOSE_ERROR(err, CJOSE_ERR_NO_MEMORY);
164
0
        return false;
165
0
    }
166
167
0
    return true;
168
0
}
169
170
////////////////////////////////////////////////////////////////////////////////
171
const char *cjose_header_get(cjose_header_t *header, const char *attr, cjose_err *err)
172
18.6k
{
173
18.6k
    if (NULL == header || NULL == attr)
174
0
    {
175
0
        CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG);
176
0
        return NULL;
177
0
    }
178
179
18.6k
    json_t *value_obj = json_object_get((json_t *)header, attr);
180
18.6k
    if (NULL == value_obj)
181
6.21k
    {
182
6.21k
        return NULL;
183
6.21k
    }
184
185
12.4k
    return json_string_value(value_obj);
186
18.6k
}
187
188
////////////////////////////////////////////////////////////////////////////////
189
bool cjose_header_set_raw(cjose_header_t *header, const char *attr, const char *value, cjose_err *err)
190
0
{
191
0
    if (NULL == header || NULL == attr || NULL == value)
192
0
    {
193
0
        CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG);
194
0
        return false;
195
0
    }
196
197
0
    json_error_t j_err;
198
    // JSON_DECODE_ANY: the documented contract accepts any valid JSON value,
199
    // including a top-level scalar (e.g. RFC 7797 "b64":false), which jansson's
200
    // default (RFC 4627) mode rejects
201
0
    json_t *value_obj = json_loads(value, JSON_DECODE_ANY, &j_err);
202
0
    if (NULL == value_obj)
203
0
    {
204
        // unfortunately, it's not possible to tell whether the error is due
205
        // to syntax, or memory shortage. See https://github.com/akheron/jansson/issues/352
206
0
        CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG);
207
0
        return false;
208
0
    }
209
210
    // json_object_set_new fails on OOM or an invalid attr key, and releases
211
    // value_obj either way; don't report success with the attribute unset
212
0
    if (0 != json_object_set_new((json_t *)header, attr, value_obj))
213
0
    {
214
0
        CJOSE_ERROR(err, CJOSE_ERR_NO_MEMORY);
215
0
        return false;
216
0
    }
217
218
0
    return true;
219
0
}
220
221
////////////////////////////////////////////////////////////////////////////////
222
char *cjose_header_get_raw(cjose_header_t *header, const char *attr, cjose_err *err)
223
0
{
224
0
    if (NULL == header || NULL == attr)
225
0
    {
226
0
        CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG);
227
0
        return NULL;
228
0
    }
229
230
0
    json_t *value_obj = json_object_get((json_t *)header, attr);
231
0
    if (NULL == value_obj)
232
0
    {
233
0
        return NULL;
234
0
    }
235
236
    // JSON_ENCODE_ANY so a top-level scalar value (see cjose_header_set_raw)
237
    // round-trips instead of returning NULL
238
0
    return json_dumps(value_obj, JSON_COMPACT | JSON_PRESERVE_ORDER | JSON_ENCODE_ANY);
239
0
}