aboutsummaryrefslogtreecommitdiffstats
path: root/include/internal/json_enc.h
blob: 5767b3e575c9ae5b600ccc9ae749253ee2531ce6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
/*
 * Copyright 2023 The OpenSSL Project Authors. All Rights Reserved.
 *
 * Licensed under the Apache License 2.0 (the "License").  You may not use
 * this file except in compliance with the License.  You can obtain a copy
 * in the file LICENSE in the source distribution or at
 * https://www.openssl.org/source/license.html
 */

#ifndef OSSL_JSON_ENC_H
# define OSSL_JSON_ENC_H

# include <openssl/bio.h>

/*
 * JSON Encoder
 * ============
 *
 * This JSON encoder is used for qlog. It supports ordinary JSON (RFC 7159),
 * JSON-SEQ (RFC 7464) and I-JSON (RFC 7493). It supports only basic ASCII.
 */

struct json_write_buf {
    BIO     *bio;
    char    *buf;
    size_t  alloc, cur;
};

typedef struct ossl_json_enc_st {
    uint32_t                flags;
    /* error: 1 if an error has occurred. */
    /* state: current state. */
    /* stack stores a bitmap. 0=object, 1=array. */
    /* stack cur   size: stack_end_byte bytes, stack_end_bit bits. */
    /* stack alloc size: stack_bytes bytes. */
    unsigned char           error, stack_end_bit, state, *stack, defer_indent;
    unsigned char           stack_small[16];
    struct json_write_buf   wbuf;
    size_t                  stack_end_byte, stack_bytes;
} OSSL_JSON_ENC;

/*
 * ossl_json_init
 * --------------
 *
 * Initialises a JSON encoder.
 *
 * If the flag OSSL_JSON_FLAG_SEQ is passed, the output is in JSON-SEQ. The
 * caller should use the encoder as though it is encoding members of a JSON
 * array (but without calling ossl_json_array_begin() or ossl_json_array_end()).
 * Each top-level JSON item (e.g. JSON object) encoded will be separated
 * correctly as per the JSON-SEQ format.
 *
 * If the flag OSSL_JSON_FLAG_SEQ is not passed, the output is in JSON format.
 * Generally the caller should encode only a single output item (e.g. a JSON
 * object).
 *
 * By default, JSON output is maximally compact. If OSSL_JSON_FLAG_PRETTY is
 * set, JSON/JSON-SEQ output is spaced for optimal human readability.
 *
 * If OSSL_JSON_FLAG_IJSON is set, integers outside the range `[-2**53 + 1,
 * 2**53 - 1]` are automatically converted to decimal strings before
 * serialization.
 */
#define OSSL_JSON_FLAG_NONE    0
#define OSSL_JSON_FLAG_SEQ     (1U << 0)
#define OSSL_JSON_FLAG_PRETTY  (1U << 1)
#define OSSL_JSON_FLAG_IJSON   (1U << 2)

int ossl_json_init(OSSL_JSON_ENC *json, BIO *bio, uint32_t flags);

/*
 * ossl_json_cleanup
 * -----------------
 *
 * Destroys a JSON encoder.
 */
void ossl_json_cleanup(OSSL_JSON_ENC *json);

/*
 * ossl_json_reset
 * ---------------
 *
 * Resets a JSON encoder, as though it has just been initialised, allowing it
 * to be used again for new output syntactically unrelated to any previous
 * output. This is similar to calling ossl_json_cleanup followed by
 * ossl_json_init but may allow internal buffers to be reused.
 *
 * If the JSON encoder has entered an error state, this function MAY allow
 * recovery from this error state, in which case it will return 1. If this
 * function returns 0, the JSON encoder is unrecoverable and
 * ossl_json_cleanup() must be called.
 *
 * Automatically calls ossl_json_flush().
 */
int ossl_json_reset(OSSL_JSON_ENC *json);

/*
 * ossl_json_flush
 * ---------------
 *
 * Flushes the JSON encoder, ensuring that any residual bytes in internal
 * buffers are written to the provided sink BIO. Flushing may also happen
 * autonomously as buffers are filled, but the caller must use this function
 * to guarantee all data has been flushed.
 */
int ossl_json_flush(OSSL_JSON_ENC *json);

/*
 * ossl_json_flush_cleanup
 * -----------------------
 *
 * Tries to flush as in a call to ossl_json_flush, and then calls
 * ossl_json_cleanup regardless of the result. The result of the flush call is
 * returned.
 */
int ossl_json_flush_cleanup(OSSL_JSON_ENC *json);

/*
 * ossl_json_set0_sink
 * -------------------
 *
 * Changes the sink used by the JSON encoder.
 */
int ossl_json_set0_sink(OSSL_JSON_ENC *json, BIO *bio);

/*
 * ossl_json_in_error
 * ------------------
 *
 * To enhance the ergonomics of the JSON API, the JSON object uses an implicit
 * error tracking model. When a JSON API call fails (for example due to caller
 * error, such as trying to close an array which was not opened), the JSON
 * object enters an error state and all further calls are silently ignored.
 *
 * The caller can detect this condition after it is finished making builder
 * calls to the JSON object by calling this function. This function returns 1
 * if an error occurred. At this point the caller's only recourse is to call
 * ossl_json_reset() or ossl_json_cleanup().
 *
 * Note that partial (i.e., invalid) output may still have been sent to the BIO
 * in this case. Since the amount of output which can potentially be produced
 * by a JSON object is unbounded, it is impractical to buffer it all before
 * flushing. It is expected that errors will ordinarily be either caller errors
 * (programming errors) or BIO errors.
 */
int ossl_json_in_error(OSSL_JSON_ENC *json);

/*
 * JSON Builder Calls
 * ==================
 *
 * These functions are used to build JSON output. The functions which have
 * begin and end function pairs must be called in correctly nested sequence.
 * When writing an object, ossl_json_key() must be called exactly once before
 * each call to write a JSON item.
 *
 * The JSON library takes responsibility for enforcing correct usage patterns.
 * If a call is made that does not correspond to the JSON syntax, the JSON
 * object enters the error state and all subsequent calls are ignored.
 *
 * In JSON-SEQ mode, the caller should act as though the library implicitly
 * places all calls between an ossl_json_array_begin() and
 * ossl_json_array_end() pair; for example, the normal usage pattern would be
 * to call ossl_json_object_begin() followed by ossl_json_object_end(), in
 * repeated sequence.
 *
 * The library does not enforce non-generation of duplicate keys. Avoiding this
 * is the caller's responsibility. It is also the caller's responsibility to
 * pass valid UTF-8 strings. All other forms of invalid output will cause an
 * error. Note that due to the immediate nature of the API, partial output may
 * have already been generated in such a case.
 */

/* Begin a new JSON object. */
void ossl_json_object_begin(OSSL_JSON_ENC *json);

/* End a JSON object. Must be matched with a call to ossl_json_object_begin(). */
void ossl_json_object_end(OSSL_JSON_ENC *json);

/* Begin a new JSON array. */
void ossl_json_array_begin(OSSL_JSON_ENC *json);

/* End a JSON array. Must be matched with a call to ossl_json_array_end(). */
void ossl_json_array_end(OSSL_JSON_ENC *json);

/*
 * Encode a JSON key within an object. Pass a zero-terminated string, which can
 * be freed immediately following the call to this function.
 */
void ossl_json_key(OSSL_JSON_ENC *json, const char *key);

/* Encode a JSON 'null' value. */
void ossl_json_null(OSSL_JSON_ENC *json);

/* Encode a JSON boolean value. */
void ossl_json_bool(OSSL_JSON_ENC *json, int value);

/* Encode a JSON integer from a uint64_t. */
void ossl_json_u64(OSSL_JSON_ENC *json, uint64_t value);

/* Encode a JSON integer from an int64_t. */
void ossl_json_i64(OSSL_JSON_ENC *json, int64_t value);

/* Encode a JSON number from a 64-bit floating point value. */
void ossl_json_f64(OSSL_JSON_ENC *json, double value);

/*
 * Encode a JSON UTF-8 string from a zero-terminated string. The string passed
 * can be freed immediately following the call to this function.
 */
void ossl_json_str(OSSL_JSON_ENC *json, const char *str);

/*
 * Encode a JSON UTF-8 string from a string with the given length. The string
 * passed can be freed immediately following the call to this function.
 */
void ossl_json_str_len(OSSL_JSON_ENC *json, const char *str, size_t str_len);

/*
 * Encode binary data as a lowercase hex string. data_len is the data length in
 * bytes.
 */
void ossl_json_str_hex(OSSL_JSON_ENC *json, const void *data, size_t data_len);

#endif