Coverage Report

Created: 2026-09-27 07:07

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
static const char *CJOSE_HDR_CRIT = "crit";
15
16
////////////////////////////////////////////////////////////////////////////////
17
bool _cjose_header_validate_crit(cjose_header_t *header, const char *const *supported, size_t supported_len, cjose_err *err)
18
0
{
19
0
    if (NULL == header)
20
0
    {
21
0
        return true;
22
0
    }
23
24
0
    json_t *crit = json_object_get((json_t *)header, CJOSE_HDR_CRIT);
25
0
    if (NULL == crit)
26
0
    {
27
0
        return true;
28
0
    }
29
30
0
    if (!json_is_array(crit))
31
0
    {
32
0
        CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG);
33
0
        return false;
34
0
    }
35
36
    // RFC 7515 section 4.1.11: if present, the "crit" list MUST NOT be empty
37
0
    if (0 == json_array_size(crit))
38
0
    {
39
0
        CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG);
40
0
        return false;
41
0
    }
42
43
0
    size_t index = 0;
44
0
    json_t *entry = NULL;
45
0
    json_array_foreach(crit, index, entry)
46
0
    {
47
0
        if (!json_is_string(entry))
48
0
        {
49
0
            CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG);
50
0
            return false;
51
0
        }
52
53
0
        const char *name = json_string_value(entry);
54
0
        bool found = false;
55
0
        for (size_t i = 0; i < supported_len; i++)
56
0
        {
57
0
            if (0 == strcmp(name, supported[i]))
58
0
            {
59
0
                found = true;
60
0
                break;
61
0
            }
62
0
        }
63
64
0
        if (!found)
65
0
        {
66
0
            CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG);
67
0
            return false;
68
0
        }
69
0
    }
70
71
0
    return true;
72
0
}
73
74
////////////////////////////////////////////////////////////////////////////////
75
bool _cjose_header_validate_disjoint(cjose_header_t *const *headers, size_t headers_len, cjose_err *err)
76
0
{
77
0
    if (NULL == headers)
78
0
    {
79
0
        return true;
80
0
    }
81
82
0
    for (size_t i = 0; i < headers_len; i++)
83
0
    {
84
0
        if (NULL == headers[i])
85
0
        {
86
0
            continue;
87
0
        }
88
89
0
        const char *name = NULL;
90
0
        json_t *value = NULL;
91
0
        json_object_foreach((json_t *)headers[i], name, value)
92
0
        {
93
0
            for (size_t j = i + 1; j < headers_len; j++)
94
0
            {
95
0
                if (NULL != headers[j] && NULL != json_object_get((json_t *)headers[j], name))
96
0
                {
97
0
                    CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG);
98
0
                    return false;
99
0
                }
100
0
            }
101
0
        }
102
0
    }
103
104
0
    return true;
105
0
}
106
107
////////////////////////////////////////////////////////////////////////////////
108
cjose_header_t *cjose_header_new(cjose_err *err)
109
0
{
110
0
    cjose_header_t *retval = (cjose_header_t *)json_object();
111
0
    if (NULL == retval)
112
0
    {
113
0
        CJOSE_ERROR(err, CJOSE_ERR_NO_MEMORY);
114
0
    }
115
0
    return retval;
116
0
}
117
118
////////////////////////////////////////////////////////////////////////////////
119
cjose_header_t *cjose_header_retain(cjose_header_t *header)
120
0
{
121
0
    if (NULL != header)
122
0
    {
123
0
        header = (cjose_header_t *)json_incref((json_t *)header);
124
0
    }
125
0
    return header;
126
0
}
127
128
////////////////////////////////////////////////////////////////////////////////
129
void cjose_header_release(cjose_header_t *header)
130
0
{
131
0
    if (NULL != header)
132
0
    {
133
0
        json_decref((json_t *)header);
134
0
    }
135
0
}
136
137
////////////////////////////////////////////////////////////////////////////////
138
bool cjose_header_set(cjose_header_t *header, const char *attr, const char *value, cjose_err *err)
139
0
{
140
0
    if (NULL == header || NULL == attr || NULL == value)
141
0
    {
142
0
        CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG);
143
0
        return false;
144
0
    }
145
146
0
    json_t *value_obj = json_string(value);
147
0
    if (NULL == value_obj)
148
0
    {
149
0
        CJOSE_ERROR(err, CJOSE_ERR_NO_MEMORY);
150
0
        return false;
151
0
    }
152
153
    // json_object_set_new fails on OOM or an invalid attr key, and releases
154
    // value_obj either way; don't report success with the attribute unset
155
0
    if (0 != json_object_set_new((json_t *)header, attr, value_obj))
156
0
    {
157
0
        CJOSE_ERROR(err, CJOSE_ERR_NO_MEMORY);
158
0
        return false;
159
0
    }
160
161
0
    return true;
162
0
}
163
164
////////////////////////////////////////////////////////////////////////////////
165
const char *cjose_header_get(cjose_header_t *header, const char *attr, cjose_err *err)
166
0
{
167
0
    if (NULL == header || NULL == attr)
168
0
    {
169
0
        CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG);
170
0
        return NULL;
171
0
    }
172
173
0
    json_t *value_obj = json_object_get((json_t *)header, attr);
174
0
    if (NULL == value_obj)
175
0
    {
176
0
        return NULL;
177
0
    }
178
179
0
    return json_string_value(value_obj);
180
0
}
181
182
////////////////////////////////////////////////////////////////////////////////
183
bool cjose_header_set_raw(cjose_header_t *header, const char *attr, const char *value, cjose_err *err)
184
0
{
185
0
    if (NULL == header || NULL == attr || NULL == value)
186
0
    {
187
0
        CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG);
188
0
        return false;
189
0
    }
190
191
0
    json_error_t j_err;
192
    // JSON_DECODE_ANY: the documented contract accepts any valid JSON value,
193
    // including a top-level scalar (e.g. RFC 7797 "b64":false), which jansson's
194
    // default (RFC 4627) mode rejects
195
0
    json_t *value_obj = json_loads(value, JSON_DECODE_ANY, &j_err);
196
0
    if (NULL == value_obj)
197
0
    {
198
        // unfortunately, it's not possible to tell whether the error is due
199
        // to syntax, or memory shortage. See https://github.com/akheron/jansson/issues/352
200
0
        CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG);
201
0
        return false;
202
0
    }
203
204
    // json_object_set_new fails on OOM or an invalid attr key, and releases
205
    // value_obj either way; don't report success with the attribute unset
206
0
    if (0 != json_object_set_new((json_t *)header, attr, value_obj))
207
0
    {
208
0
        CJOSE_ERROR(err, CJOSE_ERR_NO_MEMORY);
209
0
        return false;
210
0
    }
211
212
0
    return true;
213
0
}
214
215
////////////////////////////////////////////////////////////////////////////////
216
char *cjose_header_get_raw(cjose_header_t *header, const char *attr, cjose_err *err)
217
0
{
218
0
    if (NULL == header || NULL == attr)
219
0
    {
220
0
        CJOSE_ERROR(err, CJOSE_ERR_INVALID_ARG);
221
0
        return NULL;
222
0
    }
223
224
0
    json_t *value_obj = json_object_get((json_t *)header, attr);
225
0
    if (NULL == value_obj)
226
0
    {
227
0
        return NULL;
228
0
    }
229
230
    // JSON_ENCODE_ANY so a top-level scalar value (see cjose_header_set_raw)
231
    // round-trips instead of returning NULL
232
0
    return json_dumps(value_obj, JSON_COMPACT | JSON_PRESERVE_ORDER | JSON_ENCODE_ANY);
233
0
}